You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+282-7Lines changed: 282 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -78,7 +78,7 @@ Please find more detail about the unittest framework from the following resource
78
78
79
79
The [Refinitiv Data Platform (RDP) APIs](https://developers.refinitiv.com/en/api-catalog/refinitiv-data-platform/refinitiv-data-platform-apis) provide various Refinitiv data and content for developers via easy-to-use Web-based API.
80
80
81
-
RDP APIs give developers seamless and holistic access to all of the Refinitiv content such as Historical Pricing, Environmental Social and Governance (ESG), News, Research, etc, and commingled with their content, enriching, integrating, and distributing the data through a single interface, delivered wherever they need it. The RDP APIs delivery mechanisms are the following:
81
+
RDP APIs give developers seamless and holistic access to all of the Refinitiv content such as Environmental Social and Governance (ESG), News, Research, etc, and commingled with their content, enriching, integrating, and distributing the data through a single interface, delivered wherever they need it. The RDP APIs delivery mechanisms are the following:
82
82
* Request - Response: RESTful web service (HTTP GET, POST, PUT or DELETE)
83
83
* Alert: delivery is a mechanism to receive asynchronous updates (alerts) to a subscription.
84
84
* Bulks: deliver substantial payloads, like the end-of-day pricing data for the whole venue.
@@ -275,14 +275,16 @@ The ```setUpClass()``` is a class method that is called only onces for the whole
275
275
276
276
Note: The counterpart method of ```setUpClass()``` method is ```tearDownClass()``` which is called after all tests in the class have run.
277
277
278
-
The ```test_login_rdp_success()``` is a test case that for the success RDP Authentication login scenario. It just send the RDP Auth Service URL and RDP credentials to the ```rdp_authentication()``` method and check the response token information. Please noticed that a unit test just focusing on if the rdp_authentication() returns none empty/zero token information only. The token content validation would be in a system test (or later) phase.
278
+
The ```test_login_rdp_success()``` is a test case for the success RDP Authentication login scenario. It just send the RDP Auth Service URL and RDP credentials to the ```rdp_authentication()``` method and check the response token information. Please noticed that a unit test just focusing on if the rdp_authentication() returns none empty/zero token information only. The token content validation would be in a system test (or later) phase.
279
279
280
280
```
281
281
self.assertIsNotNone(access_token) # Check if access_token is not None or Empty
282
282
self.assertIsNotNone(refresh_token) # Check if refresh_token is not None or Empty
283
283
self.assertGreater(expires_in, 0) # Check if expires_in is greater then 0
284
284
```
285
285
286
+
Please see more detail about the supported assertion methods on the [unittest framework](https://docs.python.org/3.9/library/unittest.html) page.
287
+
286
288
The ```test_login_rdp_none_empty_params()``` is a test case that check if the ```rdp_authentication()``` method handles empty or none parameters as expected (throws the TypeError exception and not return token information to a caller).
287
289
288
290
```
@@ -330,19 +332,292 @@ Ran 2 tests in 0.816s
330
332
FAILED (errors=1)
331
333
```
332
334
333
-
However, the test suite above make HTTP requests to RDP APIs in every run. It is not a good idea to flood HTTP requests to RDP APIs repeatedly every time developers run test suite after they updated the code or configurations.
335
+
However, the test suite above make HTTP requests to RDP APIs in every run. It is not a good idea to flood requests to external services every time developers run test suite when they have updated the code or configurations.
334
336
335
-
Unit test cases should be able to run independently without rely on external services, APIs, or components. Those external dependencies add uncontrolled factors (network, data reliability, behaviors, etc) to the unit test scenarios. Those components-to-components testing should be done in integration testing phase.
337
+
Unit test cases should be able to run independently without rely on external servicesor APIs. The external dependencies add uncontrolled factors (such as network connection, data reliability, etc) to unit test cases. Those components-to-components testing should be done in an integration testing phase.
336
338
337
339
So, how can we unit test HTTP request methods calls without sending any HTTP request messages to an actual server? Fortunately, developers can simulate the HTTP request and response messages with a Mock object.
338
340
339
341
## Mocking Python HTTP API call with Responses
340
342
341
-
A mock is a fake object that you construct to look and act like real data within a testing environment. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.
343
+
A mock is a fake object that is constructed to look and act like real data within a testing environment. We can simulates various scenario of the real data with mock object, then use a mock library to trick the system into thinking that that mock is the real one.
344
+
345
+
The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies. By mocking out external dependencies, developers can run tests as often without being affected by any unexpected changes or irregularities of those dependencies. Mocking also helps developers save time and computing resources if they have to test HTTP requests that fetch a lot of data.
346
+
347
+
This example project uses the [Responses](https://github.com/getsentry/responses) library for mocking the Requests library.
348
+
349
+
So, I will start off with a mock object for testing a success RDP login case. Firstly, create a *rdp_test_auth_fixture.json* fixture file with a dummy content of the RDP authentication success response message in a *tests/fixtures* folder.
Next, load this *rdp_test_auth_fixture.json* file in the ```setUpClass()``` method to a ```mock_valid_auth_json``` class variable. The other test cases can use this mock json object for the dummy access token information.
The Responses library lets developers register mock responses to the Requests library and cover test method with ```responses.activate``` decorator. Developers can specify the endpoint URL, HTTP method, status response, response message, etc of that request via a ```responses.add()``` method.
393
+
394
+
Example Code:
395
+
396
+
```
397
+
#test_rdp_http_controller.py
398
+
399
+
import unittest
400
+
import responses
401
+
import requests
402
+
import json
403
+
import sys
404
+
import os
405
+
406
+
...
407
+
408
+
class TestRDPHTTPController(unittest.TestCase):
409
+
410
+
# A class method called before tests in an individual class are run
411
+
@classmethod
412
+
def setUpClass(cls):
413
+
...
414
+
415
+
416
+
@responses.activate
417
+
def test_login_rdp_success(self):
418
+
"""
419
+
Test that it can logged in to the RDP Auth Service (using Mock)
The code above set a Responses mock object with the *https://api.refinitiv.com/auth/oauth2/v1/token* URL and HTTP *POST* method. The Requests library then returns a ```mock_valid_auth_json``` JSON message with HTTP status *200* and Content-Type *application/json* to the application for all HTTP *POST* request messages to *https://api.refinitiv.com/auth/oauth2/v1/token* URL without any network operations between the machine and the actual RDP endpoint.
We can simulates various scenario of the real data with mock object, then use a mock library to trick the system into thinking that that mock is the real data. By mocking out external dependencies, developers can run tests as often without being affected by any unexpected changes or irregularities of those dependencies. Mocking also helps developers save time and computing resources if they have to test HTTP requests that fetch a lot of data.
This example project uses the [Responses](https://github.com/getsentry/responses) library which is specifically for mocking the Requests library.
506
+
```
507
+
508
+
The ```test_login_rdp_invalid()``` method is a test case for the failure RDP Authentication login scenario. We set a Responses mock object for the *https://api.refinitiv.com/auth/oauth2/v1/token* URL and HTTP *POST* method with the expected error response message and status (401 - Unauthorized).
509
+
510
+
Once the ```rdp_authentication()``` method is called, the test case verify if the method raises the ```requests.exceptions.HTTPError``` exception with the expected error message and status. The test case also make assertions to check if the method not return token information to a caller.
511
+
512
+
With mocking, a test case never need to send actual request messages to the RDP APIs, so we can test more scenarios for other RDP services too.
513
+
514
+
## <aid="rdp_get_data"></a>Unit Testing for RDP APIs Data Request
515
+
516
+
That brings us to requesting the RDP APIs data. All subsequent REST API calls use the Access Token via the *Authorization* HTTP request message header as shown below to get the data.
517
+
- Header:
518
+
* Authorization = ```Bearer <RDP Access Token>```
519
+
520
+
Please notice *the space* between the ```Bearer``` and ```RDP Access Token``` values.
521
+
522
+
The application then creates a request message in a JSON message format or URL query parameter based on the interested service and sends it as an HTTP request message to the Service Endpoint. Developers can get RDP APIs the Service Endpoint, HTTP operations, and parameters from Refinitiv Data Platform's [API Playground page](https://api.refinitiv.com/) - which is an interactive documentation site developers can access once they have a valid Refinitiv Data Platform account.
523
+
524
+
The example console application consume content from the following the RDP Services:
525
+
- ESG Service ```/data/environmental-social-governance/<version>/views/scores-full``` endpoint that provides full coverage of Refinitiv's proprietary ESG Scores with full history for consumers.
526
+
- Discovery Search Explore Service ```/discover/search/<version>/explore``` endpoint that explore Refinitiv data based on searching options.
527
+
528
+
However, this development article covers the ESG Service test cases only. The Discovery Search Explore Service's test cases have the same test logic as the ESG's test cases.
529
+
530
+
## Testing ESG Data
531
+
532
+
Now let me turn to testing the Environmental Social and Governance (ESG) service endpoint.
533
+
534
+
### Testing a valid RDP ESG Request-Response
535
+
536
+
I will begin by creating a fixture file with a valid ESG dummy response message. A file is *rdp_test_esg_fixture.json* in a *tests/fixtures* folder.
537
+
538
+
```
539
+
{
540
+
"links": {
541
+
"count": 5
542
+
},
543
+
"variability": "variable",
544
+
"universe": [
545
+
{
546
+
"Instrument": "TEST.RIC",
547
+
"Company Common Name": "TEST ESG Data",
548
+
"Organization PermID": "XXXXXXXXXX",
549
+
"Reporting Currency": "USD"
550
+
}
551
+
],
552
+
"data": [
553
+
[
554
+
"TEST.RIC",
555
+
"2021-12-31",
556
+
99.9999999999999,
557
+
99.9999999999999,
558
+
...
559
+
],
560
+
...
561
+
],
562
+
...
563
+
,
564
+
"headers": [
565
+
....
566
+
{
567
+
"name": "TEST 1",
568
+
"title": "ESG Score",
569
+
"type": "number",
570
+
"decimalChar": ".",
571
+
"description": "TEST description"
572
+
}...
573
+
]
574
+
}
575
+
```
576
+
Next, create the ```test_request_esg()``` method in test_rdp_http_controller.py file to test the valid ESG data request-response test case.
577
+
578
+
```
579
+
#test_rdp_http_controller.py
580
+
...
581
+
582
+
class TestRDPHTTPController(unittest.TestCase):
583
+
584
+
# A class method called before tests in an individual class are run
0 commit comments