In pytest fixtures nuts and bolts, I noted that you can specify session scope so that a fixture will only run once per test session and be available across multiple test functions, classes, and modules.

In this post, I’m going to show a simple example so you can see it in action.



Here’s the table from the previous post:

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

A separate file for fixtures, conftest.py

With function, class, and module scope, it is completely reasonable for the fixture code to be in the same file as the tests.

But now suddenly, with session, that doesn’t make sense anymore.

We can put them in conftest.py. This is a special named file that pytest looks for.

The documentation says that it’s for local plugins, but we can use it for local fixtures as well. See the pytest.org site for placement and scope of conftest.py.

Simple example of session scope fixtures

I think it’s clearest to just see this in action.

I’ve got 4 files:

conftest.py 2 fixtures my_own_session_run_at_beginning, an autouse fixture with session scope some_resource, a normal non-autouse fixture with session scope

test_alpha.py 2 simple test functions test_alpha_1, has no named fixtures test_alpha_2, has one named fixture, some_resource

test_beta.py similar to test_alpha.py, but with unittest based tests

test_gamma.py similar to test_alpha.py, but with class based tests



conftest.py:

import pytest @pytest.fixture(scope="session", autouse=True) def my_own_session_run_at_beginning(request): print('

In my_own_session_run_at_beginning()') def my_own_session_run_at_end(): print('In my_own_session_run_at_end()') request.addfinalizer(my_own_session_run_at_end) @pytest.fixture(scope="session") def some_resource(request): print('

In some_resource()') def some_resource_fin(): print('

In some_resource_fin()') request.addfinalizer(some_resource_fin)

test_alpha.py:

def test_alpha_1(): print('

In test_alpha_1()') def test_alpha_2(some_resource): print('

In test_alpha_2()')

test_beta.py:

import unittest import pytest class BetaTest(unittest.TestCase): def test_unit_beta_1(self): print('

In test_unit_beta_1()') @pytest.mark.usefixtures('some_resource') def test_unit_beta_2(self): print('

In test_unit_beta_2()')

test_gamma.py:

class TestGamma: def test_gamma_1(self): print('

In test_gamma_1()') def test_gamma_2(self, some_resource): print('

In test_gamma_2()')

Output

Run with pytest -s -v

============================= test session starts ============================== platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python collecting ... collected 6 items test_alpha.py:1: test_alpha_1 In my_own_session_run_at_beginning() In test_alpha_1() PASSED test_alpha.py:4: test_alpha_2 In some_resource() In test_alpha_2() PASSED test_beta.py:5: BetaTest.test_unit_beta_1 In test_unit_beta_1() PASSED test_beta.py:8: BetaTest.test_unit_beta_2 In test_unit_beta_2() PASSED test_gamma.py:2: TestGamma.test_gamma_1 In test_gamma_1() PASSED test_gamma.py:5: TestGamma.test_gamma_2 In test_gamma_2() PASSED In some_resource_fin() In my_own_session_run_at_end() =========================== 6 passed in 0.04 seconds ===========================

Mixing function, module, and session scope

Let’s say I’ve got:

a function scope fixture ‘resource_c’

that uses a module scoped fixture ‘fixture_b’

that uses a session scoped fixture ‘fixture_a’

This all works fine.

Also in this example, I’ve added a few autouse fixtures just for fun.

conftest.py:

import pytest @pytest.fixture(scope="session") def resource_a(request): print('In resource_a()') def resource_a_fin(): print('

In resource_a_fin()') request.addfinalizer(resource_a_fin) @pytest.fixture(scope="module") def resource_b(request, resource_a): print('In resource_b()') def resource_b_fin(): print('

In resource_b_fin()') request.addfinalizer(resource_b_fin) @pytest.fixture(scope="function") def resource_c(request, resource_b): print('In resource_c()') def resource_c_fin(): print('

In resource_c_fin()') request.addfinalizer(resource_c_fin) # these are just some fun dividiers to make the output pretty # completely unnecessary, I was just playing with autouse fixtures @pytest.fixture(scope="function", autouse=True) def divider_function(request): print('

--- function %s() start ---' % request.function.__name__) def fin(): print(' --- function %s() done ---' % request.function.__name__) request.addfinalizer(fin) @pytest.fixture(scope="module", autouse=True) def divider_module(request): print('

------- module %s start ---------' % request.module.__name__) def fin(): print(' ------- module %s done ---------' % request.module.__name__) request.addfinalizer(fin) @pytest.fixture(scope="session", autouse=True) def divider_session(request): print('

----------- session start ---------------') def fin(): print('----------- session done ---------------') request.addfinalizer(fin)

test_one_two.py:

def test_one(resource_c): print('In test_one()') def test_two(resource_c): print('

In test_two()')

test_three_four.py:

def test_three(resource_c): print('

In test_three()') def test_four(resource_c): print('

In test_four()')

This seems reasonable to me.

What do you think will happen?

output:

$ py.test -s -v ==================================== test session starts ==================================== platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python collected 4 items test_one_two.py:1: test_one ----------- session start --------------- ------- module test_one_two start --------- --- function test_one() start --- In resource_a() In resource_b() In resource_c() In test_one() PASSED In resource_c_fin() --- function test_one() done --- test_one_two.py:4: test_two --- function test_two() start --- In resource_c() In test_two() PASSED In resource_c_fin() --- function test_two() done --- In resource_b_fin() ------- module test_one_two done --------- test_three_four.py:1: test_three ------- module test_three_four start --------- --- function test_three() start --- In resource_b() In resource_c() In test_three() PASSED In resource_c_fin() --- function test_three() done --- test_three_four.py:4: test_four --- function test_four() start --- In resource_c() In test_four() PASSED In resource_c_fin() --- function test_four() done --- In resource_b_fin() ------- module test_three_four done --------- In resource_a_fin() ----------- session done --------------- ================================= 4 passed in 0.02 seconds ==================================

WARNING: you gotta use bigger and bigger scope

If you do this in the wrong order, things go haywire.

Let’s swap scope on a couple of items.

conftest.py:

... @pytest.fixture(scope="module") # session -> module def resource_a(request): print('In resource_a()') def resource_a_fin(): print('

In resource_a_fin()') request.addfinalizer(resource_a_fin) @pytest.fixture(scope="session") # module -> session def resource_b(request, resource_a): ...

We will get some warning like this (or several):

E ScopeMismatchError: You tried to access the 'module' scoped fixture 'resource_a' ... with a 'session' scoped request object, involved factories E conftest.py:18: def resource_c(request, resource_b) E conftest.py:10: def resource_b(request, resource_a) E conftest.py:2: def resource_a(request)

So. Don’t do that.

Warning applies to built in fixtures

Pytest includes some built in fixtures. I believe all of them are function scoped.

This means that you cannot use them from anything other than functions or function scoped fixtures.

Taking it further

The code I’ve shown is for simple run at the beginning and end type fixtures.

However, there’s more you can do with session fixtures.

The pytest.org site has a cool example, A session-fixture which can look at all collected tests.

What are you using session fixtures for?

I’d love to hear examples and use cases for session fixtures.

Please leave a comment or let me know @brianokken of how you are using them.