We use the pytest framework for writing tests. A helpful resource for a list of supported assertions can be found here. We also use tox for automating tests.
Ensure that all tests pass when you run tox
in the main folder.
Alternatively, one can run py.test <path_to_test>
if you are trying to test a specific module.
Helper functions should go into the helpers folder, and autoprotocol json in the autoprotocol folder.
The conftest.py
file contains any pytest related configurations.
Pytest Fixtures are used through out the package to reduce the need for duplicated code. This is also used for environment setup/teardown effects. There are a couple of common fixtures shared throughout the test package.
test_api
: This sets up a test Connection and monkeypatches the api_req_call
function with an offline/mock equivalent and can be used in conjunction with theresponse_db
fixtureresponse_db
,json_db
: These fixtures are used for holding our MockResponse objects and Json objects respectively.
###Background Information
For API tests, we overwrite the base call command with a mock call that draws from the mockDB
dictionary.
To mock responses, we use the MockResponse
class, which basically mirrors the Response
class from the
requests library. The three key fields to mock here are the status_code
, json
and text
fields.
Similar to the Response
object, the status_code
corresponds to the HTML response codes (e.g. 404, 201),
the json
method corresponds to the json
response which is usually returned when the call is succesful.
Lastly, the text
field is what's commonly used for displaying logs/error messages.
Writing an api test
:
- First define a test class if necessary (e.g.
TestAnalyze
). This should hold all tests related to the functionality you are trying to test. - Add module wide fixtures in the
conftest.py
file. For example, thetest_api
fixture is something which will we will use for most testing and its found there. - If there is a common set of setup steps you intend to do for each test function within the class, define an
initialization function and decorate it with the
@pytest.fixture(autouse=True)
decorator. In this case, we define aninit_db
function which uses both thejson_db
andresponse_db
fixtures for getting/loading a set of json and MockResponses. - Write your test function, feeding in all the required fixtures. For example, here I would like to test the default
responses for an
analyze
call. I know I require thetest_api
,json_db
andresponse_db
fixtures, so I list them as arguments. - To test any mocked API calls, I'll need the
mockRoute
function. This mocks anycall
s made to a specificroute
with a MockResponse object. Ifmax_calls
is specified, theresponse
will be returnedmax_calls
number of times. Note that if the same route is mocked multiple times, responses will join a queue. Ifmax_calls
is not specified, the response will be set as the default response of that route. The default response will then be returned when there are no more elements in the queue. - Here, my route is given by
test_api.get_route('analyze_run')
and the call ispost
. So let's mock a route for testing 404 responses.mockRoute(post, test_api.get_route('analyze_run'), response_db["mock404"], max_calls=1)
. - To test if a 404 is indeed returned, I use the pytest syntax for checking exceptions. And I call
analyze_run
with an invalid protocol jsonwith pytest.raises(Exception):
test_api.analyze_run(json_db['invalidTransfer'])
The final code block can be seen in the TestAnalyze
class, in the api_test
module