Unit Testing

The benefits of testing your code are multifaceted, from communicating requirements to easing refactoring efforts. The SDK comes packaged with a Python unit testing package called pytest, which is full of useful features and patterns for testing your extensions. To accomplish tasks beyond the provided examples, we recommend checking out the pytest documentation. Run your tests at the command line: q2 test.

Writing Tests

What makes a good test? Like code, there’s no right or wrong way to write tests, but here are a few guidelines:

  1. Tests should be atomic: Each test should only test a single result or concept. Perhaps the state of an object after it’s created, or the return of a method.

  2. Tests should be small: Large tests require large amounts of debugging effort.

  3. Tests should be explicit: A good test does not contain logic for generating the expected outputs. That’s simply another point of failure. Nobody wants to write tests on their tests!

  4. Tests should be repeatable: Tests should behave the same way every time, containing no randomized data and no external dependencies.

SDK Tests

Each extension you create using the q2 create_extension command will have tests generated for it. It is your decision whether to update and use these as you code. Let’s take a look at one:

...

class MockFooHandler(extension.FooHandler):
    def __init__(self):
        super().__init__(Application(), RequestMock(), logging_level='INFO')


@pytest.mark.skip(reason="Generated with extension")
async def test_default_route():
    handler = MockFooHandler()
    actual = await handler.default()

    assert isinstance(actual, Q2Form)
    assert actual.routing_key == 'submit'
    assert len(actual.rows) == 1
    assert actual.hide_submit_button is False

...

Important

Notice the test itself is disabled due to the @pytest.mark.skip decorator: pytest will not run it. To enable this test, remove that line.

When we generated our extension with q2 create_extension foo, we would find this code at foo/tests/test_request_handler.py.

What does this test actually do?

First we set up an instance of our handler. We’ve created a MockFooHandler that inherits from our actual request handler– now we can test methods without actually booting up a web server.

This particular test is running through the default() method, so we call that and assign the response to the variable actual.

Finally, we assert the state of actual (returned from default()) is what we expected. If you were to change the behavior of the default() method in any way, this test would fail. If the change was purposeful, updating the test to conform is simple. If the change was accidental, having these assertions in place will ensure it is quickly discovered and corrected.

Your extension’s git commit hook runs q2 test on commit. This hook is generated when you run q2 setup, if a git repository has been initialized. If you have failing tests, your commit will be canceled.

To skip this hook, you can run git commit with the argument --no-verify to commit without testing. To remove testing from the commit hook altogether, remove the relevant lines from your .git/hooks/pre-commit file.