PyGrunn: advanced pytest - Òscar Vilaplana

Tags: pygrunn, python

(One of my summaries of a talk at the 2019 PyGrunn conference).

Imagine being a developer being woken up at night because your latest commit broke the website. You fix the issue, run the tests of your part of the code (which passes) and push to github. That runs all the tests and it fails in a completely unrelated piece of the code. But what is happening? Is the test wrong? Is your code wrong? “3 is not 90”: what does that mean?

What does it mean that this fails? What is the test’s promise? If a test you wrote fails, it should fail beautifully. It should tell exactly what’s wrong:

assert num_something == 2, "The number should match the number of added items"

You can use pytest fixtures to at least make the data the test is working with clearer.

You can make fixtures that work as context managers:

@pytest.fixture
def test_with_teardown():
    thing = create_something()  # setup
    yield thing
    thing.destroy()  # teardown

A tip: have fixtures per subsystem. Assuming you have multiple test directories, one per subsystem. Give every subsystem its own conftest.py. Different subsystems might get different fixtures even though using the same name (“product” for instance). This way you can tweak your main fixture items per subsystem.

  • Disadvantage: it is implicit instead of explicit…

  • Advantage: the fixtures can stay minimal. Otherwise your fixture has to support all use cases.

You can parametrize fixtures:

@pytest.fixture(params=["no-user", "disabled-user", "read-only-user"])
def unauthorized_user():
    if param == ...
        return ...
    if param == ...
        ...

Tests using that fixture are run three times, once for every possible kind of unauthorized user!

You can do it even more elaborate. You can make a kind of a “build matrix” and use @pytest.mark.parametrize.

If every test needs a temporary database or a temporary something, you can pass auto_use=True to the fixture, that’ll apply it automatically.

Pytest can help you with mocking, but sometimes you’re better off setting up dependency injection. So adding a parameter to the method you’re testing to accept some mocked item instead of its regular default.

If you think regular code is more important than the tests: he pro-tests :-)

You need tests because they give you a feeling of safety. If you feel safe, you dare to try things. Tests are a bit of a shared goal inside a team: you and your code want to belong. You want interaction: make sure your tests are communicative and helpful.

 
vanrees.org logo

Reinout van Rees

My name is Reinout van Rees and I program in Python, I live in the Netherlands, I cycle recumbent bikes and I have a model railway.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):