Niranjan M.R
Niranjan M.R

Reputation: 351

Managing many session-scoped fixtures in pytest

I have a below code in my conftest.py

import pytest

@pytest.fixture(scope="class")
def fix1():
   print("i am in fix1")
   a = 10
   return a

@pytest.fixture(scope="class")
def fix2():
   print("i am in fix2")
   b = 20
  return b

@pytest.fixture(scope="session",autouse=True)
def setup_session(request):
    tp = TestSetup(fix1)
    tp.setup()
    def teardown_session():
      tp.teardown()
    request.addfinalizer(teardown_session)

class TestSetup(object):
    def __init__(self, fix1, fix2):
        self.fix1 = fix1
        self.fix2 = fix2
    def setup(self):
        print("i am in setup")
        print(self.fix1)
   def teardown(self):
       print("I am in teardown")
       print(self.fix2)

   # py.test -s test1.py 
   =========== test session starts ===========
   platform linux2 -- Python 2.7.5, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
   rootdir: /tmp/test, inifile: 
   collected 2 items 

   test1.py i am in setup
   <function fix1 at 0x2948320>
   i am in test1
   .i am in test2
   .I am in teardown

when i execute the above using pytest, the fixtures fix1 and fix2 are never called. I need a way to call fix1 and fix2 as part of of setup and teardown before any of my tests runs.

What i am a trying to achieve is before any of my test runs, i need to have a setup created, the fix1, and fix2 are fixture which setup few things . I would like to have these fixture be called before any of my test runs and once all the tests are run , i call teardown function to teardown my setup.

Upvotes: 1

Views: 3954

Answers (1)

Ezequiel Muns
Ezequiel Muns

Reputation: 7742

If what you want it to create a set of fixtures that get created once per session, reused for every test and then torn down, the py.test way of doing that would be:

import pytest

@pytest.fixture(scope="session")
def fix1(request):
    # setup code here
    print('creating fix1')
    a = 10
    def teardown_fix1():
        # teardown code here
        print('destroying fix1')
        a = None
    request.addfinalizer(teardown_fix1)
    return a

def testOne(fix1):
    print('in testOne')
    assert fix1 == 10

def testTwo(fix1):
    print('in testTwo')
    assert fix1 != 20

(You already know how to do this, as shown in your code, so nothing new here).

You could have several of these session scoped fixtures, and the fact that they are session scoped guarantees that they will be created once, and then torn down at the end of the test run.

There is no need to have one master setup function that will set up every fixture and another which will tear them down, rather the idea is that you separate these into their own little factory functions with a finalizer as shown.

EDIT

But maybe there are a lot of fixtures that look pretty much the same, and you want to reuse some code. In that case, you could have a class that manages the fixtures and make that a pytest fixture. Then you declare other pytest fixtures that depend on the manager, each returning a specific fixture:

import pytest

class FixtureManager:
    def __init__(self):
        self.fixtures = {}

    def setup(self):
        print('setup')
        self.fixtures['fix1'] = self.createFixture('fix1')
        self.fixtures['fix2'] = self.createFixture('fix2')
        self.fixtures['fix3'] = self.createFixture('fix3')

    def get(self, name):
        return self.fixtures[name]

    def teardown(self):
        print('teardown')
        for name in self.fixtures.keys():
            # whatever you need to do to delete it
            del self.fixtures[name]

    def createFixture(self, name):
        # whatever code you do to create it
        return 'Fixture with name %s' % name

@pytest.fixture(scope="session")
def fixman(request):
    fixman = FixtureManager()
    fixman.setup()
    request.addfinalizer(fixman.teardown)
    return fixman

@pytest.fixture
def fix1(fixman):
    return fixman.get('fix1')

@pytest.fixture
def fix2(fixman):
    return fixman.get('fix2')

def testOne(fix1):
    print('in testOne')
    assert fix1 == 'Fixture with name fix1'

def testTwo(fix2):
    print('in testTwo')
    assert fix2 == 'Fixture with name fix2'

Of course, you could do away with creating the fix1, fix2 pytest fixtures, and get at those instead by injecting fixman into your test functions and calling get there. You be the judge what makes more sense and generates least boilerplate.

Upvotes: 0

Related Questions