pytest fixtures nuts and bolts

I’d like to wrap up this recent series of pytest fixture posts by presenting my version of some sort of reference.
Since this post is running a bit long, here are some links to the content buried in here.

Since I’m planning on using this for my own reference, I’ll throw a couple more links in here at the top to stuff I find useful regarding pytest fixtures.

And… while I’m throwing links around, here are the other posts in the series:

I’m trying to keep the examples in this post kind of small-ish.
However, I do run the risk of being too terse. (or too verbose).
It’s also possible that the order I’ve laid things out is odd. I’ve done a lot of copy/paste from code editor and bash window to get this post put together.

Please let me know:

  • if I’ve mucked up some copy/paste and there is something bizarre in here.
  • if I’ve been too terse or unclear.
  • if what I’m stating is completely wrong. Especially this one
  • if you actually made it to the end without wanting to throw something at me.

Note about commmon code

For all the examples, the test file I’m running has this at the top:

However, I’m not going to copy it into every code block below.
I’m also running each example with:

Bare bones example

Here’s a super basic fixture, and a couple tests that use it.

With the default parameters for ‘pytest.fixture()’, the fixture is going to be called for every test that names the fixture in it’s parameter list.
Output:

Three ways to use a fixture

  1. name it from the test.
    Just like the top example
  2. usefixtures decorator
    You can mark a test or a test class with ‘pytest.mark.usefixtures()’ and include a list of fixtures to be used with the test or class of tests.
    This is especially convenient when dealing with test classes.
    It also is useful when converting unittest classes to use pytest fixtures.
    I’ll give an example shortly.
  3. autouse
    Powerful, but possibly dangerous.
    Covered in the next section.

Usefixtures example

Here’s a quick example of the decorator.
The first example can be written like this:

Or, this:

Or, this:

All with the same effect.

Fixture features

If I fill in the default parameters for ‘pytest.fixture()’ and add a request param to my fixture, it looks like this, but doesn’t run any different.

Now lets take a look at these features.

Return value

In the bare bones example, the fixture returns ‘None’.
That’s becuase it’s just some code I want to run before my test, like traditional setup functions from nose or unittest.

However, you can return anything you want from the fixture function.
If your fixture is setting up some data, or reading a file, or opening a connection to a database, then access to that data or resources is what you ought to return from the fixture.

Returning some data from a fixture.

Returning a database object:

Finalizer is teardown

In the previous code example, the ‘cheese_db’ fixture has this bit of code:

The ‘fin’ function is acting as the ‘teardown’ for the fixture.
There’s nothing special about the name.
You can name it ‘teardown’ or ‘cheese_db_teardown’ or ‘something_else’.
It doesn’t matter.

The finalizer is called after all of the tests that use the fixture.
If you’ve used parameterized fixtures, the finalizer is called between instances of the parameterized fixture changes.

Scope

Scope controls how often a fixture gets called. The default is "function".
Here are the options for scope:

function Run once per test
class Run once per class of tests
module Run once per module
session Run once per session

Since the default scope is "function", the cheese db example will open and close the db for every test.

This doesn’t really make sense. Especially if connecting is a time consuming operation.

Change the scope:

And let’s re-run it:

That’s better.

Request objects

In the cheese db example, the fixture includes a request parameter. You need the request paramter to a fixture to add a finilizer.

However, it has other uses too.

In the example below, I’m showing the use (well, printing stuff) of some of the items. See pytest API for a full list.

Params

An optional parameter to the fixture decorator is ‘params’.
It defaults to ‘None’.

For each value in params, the fixture will be called with request.param filled in with that value.
Tests that use the fixture will be called once FOR EACH value in params.

Toy example

An example is in order here.

This first example is a silly one, but does show the mechanics, and the utility of both the -v flag and how well py.test deals with failures of paramterized tests.

This should run ‘test_not_2′ three times, and fail when 2 is passed in.
I’ll run it both without and with the -v flag

Real example

Now for a more real world usage of paramterization, input and expected output.

Here’s a rewrite of the markdown test from the pytest introduction post.
The ‘run_markdown’ function is a software API adapter, which takes care of calling the markdown script on the command line.

The output makes it clear that the one test ‘test_markdown’ is called 3 times.
Of course, the print statements are unnecessary.
I left them in for demo purposes.

Normally, you don’t need the print statements to see what’s going on.

I’m going to take the print statements out, and change the ‘expected’ string of the last input value to show how py.test is quite helpful in pointing out the data set that fails.

output:

Autouse

An optional parameter to the fixture decorator is ‘autouse’.
It defaults to ‘False’.

With the value of ‘False’, tests that wish to use the fixture need to either name it in their parameter list, or have a use fixtures decorator applied to the test.

See the first two items in section ‘three ways to use a fixture‘ for more information.

With the value set to ‘True’, all tests in this session just use the fixture automatically.

Yes, with great power comes great responsibility.

So use it carefully.

However, it is quite handy in places where you would have used the xunit style setup_module

For our example, let’s just say I’ve got some reporting code I’d like to run at the top of module and at the top of a test funcion.

The tests themselves don’t need a handle to the fixtures, since they aren’t returning any data.

output:

Multiple fixtures

In the examples I’ve used so far, tests only are using at most one named fixture.
You can use more.

Simple example:

output:

Modularity: fixtures using other fixtures

Tests can use one or more fixture.

Fixtures themselves can also use one or more fixtures.

I’ll rewrite the previous example, but instead of having the tests include all foo, bar, and baz fixtures, I’ll chain them together.

And one more wrinkle, ‘test_two’ will only include ‘bar’.

output

Experimental and still to cover

In this section, I’m listing the experimental features, and features I haven’t fully tested and/or don’t quite understand yet.

yield_fixture

Thank you Johannes for pointed out this feature in the comments.

You can use ‘yield_fixture’ instead of the ‘fixture’ decorator for functions that yield their value rather than returning them.

The benefits are:

  • It works like a context manager.
  • You don’t have to register a teardown function via addfinalizer.
  • Therfore, you don’t have to include the request parameter just for the addfinalizer function.

Caveats:

  • It’s still "experimental", so supposedly the syntax/behavior might change in the future.
  • Probably more, but I haven’t used it much to know what else to be careful of.

Here’s a quick example. (Yep. Staight from the comment.)

WARNING: My recommendation is to be aware of this feature, but use ‘addfinalizer’ for production test code.
This is a cool feature. But since it’s still listed as ‘experimental’, and I haven’t done much testing with it or testing of it, I can’t in good conscience recommend it’s use.

Hey pytest devs: Let me know if this WARNING is too strong.

I DO recommend you use it IFF you are either solo or on a small team where you are able to easily change the test code in the future if the syntax/behavior changes in future pytest releases.

ids

More information at pytest.org

I played around with this a bit, but couldn’t get anything to work.
I’m sure I was just doing something wrong.
If anyone has a working example they could share, please do.

Feedback

Leave a comment. Let me know if this is helpful or confusing. If you end up finding some bell or whistle of pytest fixtures that I missed, let me know.

If you like this…

Get notified of new posts, delivered to your inbox. No spam. Not very frequent.

Comments

  1. says

    It might be worth mentioning the new “yield_fixture” feature from py.test 2.4, which allows for more painless teardowns without requiring a closure and a call to request.addFinalizer:

    @pytest.yield_fixture
    def cheese_db():
    print(‘\n[setup] cheese_db, connect to db’)
    a_dictionary_for_now = {‘Brie’: ‘No.’, ‘Camenbert’: ‘Ah! We have Camenbert, yessir.’}
    # Yield the fixture
    yield a_dictionary_for_now
    # Once the function using the fixture has returned, continue with the teardown
    print(‘\n[teardown] cheese_db finalizer, disconnect from db’)

    The neat thing about this is that it makes it very easy to create fixtures that provide a context for test functions to run in. I use this a lot with the ‘mock’ package:

    @pytest.yield_fixture
    def mock_object():
    with mock.Mock(‘mypackage.someobject’) as mock:
    mock.return_value = True
    yield

    By using the “mock_object” fixture, your test will run in its context, i.e. “someobject” will be a mock, and will automatically be reset once the test function returns and the fixture generator finishes.

    More information in the docs:
    http://pytest.org/latest/yieldfixture.html#yieldfixture

  2. mamachanko says

    Hey @Brian, is the session scope of pytest fixtures referring to a single test run and thus the most general fixture scope? E.g. anything in that scope will only be created once, correct?

  3. says

    I don’t recall wanting to throw anything at you, except maybe thank-yous. I too would like to figure out session scope. My first attempt to share a session-scope fixture between modules ran it twice, to my disappointment.

  4. Himani says

    Hi Brian,
    I m using pytest approach with Page Object Module , as in i m trying to invoke classes and function from other files which are imported to the main file.
    Question –
    a. where do i need to place these setup and teardown functions? – to the main file or the one which is imported.
    b. Can we write the setup/teardown function in _init_ function ?

Leave a Reply