Managing many session-scoped fixtures in pytest - python

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.

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.

Related

Using fixtures in Pytest

I have the following code in the conftest.py file
#pytest.fixture(scope="session", autouse=True)
def app(request):
global fixture
browser = request.config.getoption("--browser")
base_url=target['baseUrl'])
fixture = Application(browser=browser,base_url=web_config['baseUrl'])
print("\n BEFORE SESSION")
fixture.session.login()
return fixture
#pytest.fixture(scope="session", autouse=True)
def stop(request):
def fin():
print("\n AFTER SESSION")
fixture.session.ensure_logout()
fixture.destroy()
request.addfinalizer(fin)
return fixture
The test file looks like this. Ie fixture, I obviously do not call.
import pytest
#pytest.yield_fixture()
def setup_method():
print("\n BEFORE METHOD")
yield
print("\n AFTER METHOD")
#pytest.mark.usefixtures("setup_method")
def test_add_text_element(app):
print("\n RUN TEST")
app.element.add_blank_page()
app.element.add_element(element_name='Header')
But what if I need to set some other class settings? If I get another fixture, how can I use it in the test file, instead of the one used now?
All about fixtures in py.test you can find in this doc. Below you can found an example how to use fixtures. First of all don't use global. Then be careful about autouse parameter of fixtures. For setup and teardown yield_fixture will be you choice. Use usefixtures as decorator for class. Class will be good to organize your test code. You can found more information about usage in this article (RUS)
conftest.py
#pytest.yield_fixture()
def destroy_method():
yield
print("\n DESTROY")
#pytest.yield_fixture(scope="session", autouse=True)
def app(request):
browser = request.config.getoption("--browser")
fixture = Application(browser=browser, base_url=web_config['baseUrl'])
print("\n BEFORE SESSION")
fixture.session.login()
yield fixture
print("\n AFTER SESSION")
fixture.session.ensure_logout()
fixture.destroy()
Test file will be looks like:
#pytest.yield_fixture()
def setup_method():
print("\n BEFORE METHOD")
yield
print("\n AFTER METHOD")
#pytest.fixture()
def fix1():
return 1
#pytest.fixture()
def fix2():
return 2
#pytest.mark.usefixtures("setup_method", "destroy_method")
class TestSuiteA:
def test_add_text_element(self, fix1, fix2):
print("\n RUN TEST")
assert fix1 + 1 == fix2
From pytest docs:
"yield_fixture" functions:
Since pytest-3.0, fixtures using
the normal fixture decorator can use a yield statement to provide
fixture values and execute teardown code, exactly like yield_fixture
in previous versions.
Marking functions as yield_fixture is still supported, but deprecated
and should not be used in new code.
Reference: https://docs.pytest.org/en/latest/yieldfixture.html

Is there a way to run "setup" only once for the entire suite of Python tests? [duplicate]

Is there a function that is fired at the beginning/end of a scenario of tests? The functions setUp and tearDown are fired before/after every single test.
I typically would like to have this:
class TestSequenceFunctions(unittest.TestCase):
def setUpScenario(self):
start() #launched at the beginning, once
def test_choice(self):
element = random.choice(self.seq)
self.assertTrue(element in self.seq)
def test_sample(self):
with self.assertRaises(ValueError):
random.sample(self.seq, 20)
for element in random.sample(self.seq, 5):
self.assertTrue(element in self.seq)
def tearDownScenario(self):
end() #launched at the end, once
For now, these setUp and tearDown are unit tests and spread in all my scenarios (containing many tests), one is the first test, the other is the last test.
As of 2.7 (per the documentation) you get setUpClass and tearDownClass which execute before and after the tests in a given class are run, respectively. Alternatively, if you have a group of them in one file, you can use setUpModule and tearDownModule (documentation).
Otherwise your best bet is probably going to be to create your own derived TestSuite and override run(). All other calls would be handled by the parent, and run would call your setup and teardown code around a call up to the parent's run method.
I have the same scenario, for me setUpClass and tearDownClass methods works perfectly
import unittest
class Test(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls._connection = createExpensiveConnectionObject()
#classmethod
def tearDownClass(cls):
cls._connection.destroy()
Here is an example: 3 test methods access a shared resource, which is created once, not per test.
import unittest
import random
class TestSimulateLogistics(unittest.TestCase):
shared_resource = None
#classmethod
def setUpClass(cls):
cls.shared_resource = random.randint(1, 100)
#classmethod
def tearDownClass(cls):
cls.shared_resource = None
def test_1(self):
print('test 1:', self.shared_resource)
def test_2(self):
print('test 2:', self.shared_resource)
def test_3(self):
print('test 3:', self.shared_resource)
For python 2.5, and when working with pydev, it's a bit hard. It appears that pydev doesn't use the test suite, but finds all individual test cases and runs them all separately.
My solution for this was using a class variable like this:
class TestCase(unittest.TestCase):
runCount = 0
def setUpClass(self):
pass # overridden in actual testcases
def run(self, result=None):
if type(self).runCount == 0:
self.setUpClass()
super(TestCase, self).run(result)
type(self).runCount += 1
With this trick, when you inherit from this TestCase (instead of from the original unittest.TestCase), you'll also inherit the runCount of 0. Then in the run method, the runCount of the child testcase is checked and incremented. This leaves the runCount variable for this class at 0.
This means the setUpClass will only be ran once per class and not once per instance.
I don't have a tearDownClass method yet, but I guess something could be made with using that counter.
import unittest
class Test(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.shared_data = "dddd"
#classmethod
def tearDownClass(cls):
cls.shared_data.destroy()
def test_one(self):
print("Test one")
def test_two(self):
print("Test 2")
For more visit Python
unit test document

How to share object from fixture to all tests using pytest?

What is the best way to define an object in a fixture with session scope and autouse=True, so it will be available to all tests?
#pytest.fixture(scope='session', autouse=True)
def setup_func(request):
obj = SomeObj()
Next thing, I want some magic that previously created obj will appear in each test context without the need of each test to define the setup_func fixture.
def test_one():
obj.do_something_fancy()
My recommendation would to add the fixture to conftest.py and make sure to return the object you want to produce from the fixture.
As noted, this makes "autouse" kind of useless.
In the root directory for your tests, add the fixture to a file named conftest.py:
#pytest.fixture(scope='session', autouse=True)
def someobj(request):
return SomeObj()
Any test file beneath the root file will have access to this fixture (for example test_foo.py):
def test_foo(someobj):
assert isinstance(someobj, SomeObj)
Another approach, would be to use a global variable defined in the same test or imported from a module.
For example in conftest.py:
someobj = None
#pytest.fixture(scope='session', autouse=True)
def prep_someobj(request):
someobj = SomeObj()
Then in your test:
from . import conftest
def test_foo():
assert isinstance(conftest.someobj, SomeObj)
In my opinion this is less readable and more cumbersome than the first method.
A more general pattern for this is to return locals() at the end of your conftest and you'll be able to easily reference anything created in the fixture.
conftest.py
#pytest.fixture(scope='session')
def setup_func(request):
obj1 = SomeObj()
obj2 = SomeObj()
return locals()
test_stuff.py
def test_one(setup_func):
setup_func['obj1'].do_something_fancy()
def test_two(setup_func):
setup_func['obj2'].do_something_fancy()
Another possibility is to wrap your tests in a class and use class variables to only define the object instance once. This assumes you are able to wrap all tests in a single class and so this answer may address a less general, but similar use case. For example,
class SomeObj():
"""This object definition may exist in another module and be imported."""
def __init__(self):
self.x = 5
def do_something_fancy(self, y):
return self.x * y
class TestX():
# Object instance to share across tests
someobj = SomeObj()
def test_x(self):
assert TestX.someobj.x == 5
def test_fancy(self):
fancy_factor = 10
result = TestX.someobj.do_something_fancy(fancy_factor)
assert result == 50

Testing methods each with a different setup/teardown

I'm testing a class, with many test methods. However, each method has a unique context. I then write my code as following:
class TestSomeClass(unittest.TestCase):
def test_a():
with a_context() as c:
pass
def test_b():
with b_context() as c:
pass
def test_c():
with c_context() as c:
pass
However, the context managers are irrelevant to the test case, and produce temporary files. So as to not pollute the file system when the test fails, I would like to use each context manager in a setup/teardown scenario.
I've looked at nose's with_setup, but the docs say that is meant for functions only, not methods. Another way is to move the test methods to separate classes each with a setup/teardown function. What's a good way to do this?
First of all, I'm not sure why what you have isn't working. I wrote some test code, and it shows that the exit code always gets called, under the unittest.main() execution environment. (Note, I did not test nose, so maybe that's why I couldn't replicate your failure.) Maybe your context manager is broken?
Here's my test:
import unittest
import contextlib
import sys
#contextlib.contextmanager
def context_mgr():
print "setting up context"
try:
yield
finally:
print "tearing down context"
class TestSomeClass(unittest.TestCase):
def test_normal(self):
with context_mgr() as c:
print "normal task"
def test_raise(self):
with context_mgr() as c:
print "raise task"
raise RuntimeError
def test_exit(self):
with context_mgr() as c:
print "exit task"
sys.exit(1)
if __name__ == '__main__':
unittest.main()
By running that with $ python test_test.py I see tearing down context for all 3 tests.
Anyway, to answer your question, if you want a separate setup and teardown for each test, then you need to put each test in its own class. You can set up a parent class to do most of the work for you, so there isn't too much extra boilerplate:
class TestClassParent(unittest.TestCase):
context_guard = context_mgr()
def setUp(self):
#do common setup tasks here
self.c = self.context_guard.__enter__()
def tearDown(self):
#do common teardown tasks here
self.context_guard.__exit__(None,None,None)
class TestA(TestClassParent):
context_guard = context_mgr('A')
def test_normal(self):
print "task A"
class TestB(TestClassParent):
context_guard = context_mgr('B')
def test_normal(self):
print "task B"
This produces the output:
$ python test_test.py
setting up context: A
task A
tearing down context: A
.setting up context: B
task B
tearing down context: B
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK

PyUnit: How to run all tests from different unittest.TestCase subclass present in a file

import unittest
import HTMLTestRunner
class TestClass1(unittest.TestCase):
def setUp(self):
pass
def case1(self):
assert 4 == 3
def case2(self):
assert 4 == 4
def tearDown(self):
pass
class TestClass2(unittest.TestCase):
def setUp(self):
pass
def case3(self):
assert 1 == 2
def tearDown(self):
pass
def suite():
suite = unittest.TestSuite()
suite.addTest(TestClass1(['case1','case2']))
suite.addTest(TestClass2('case4'))
return suite
test_suite = suite()
unittest.TextTestRunner(verbosity=2).run(test_suite)
fp = file('my_report.html', 'wb')
runner = HTMLTestRunner.HTMLTestRunner(
stream=fp,
title='My unit test',
description='This demonstrates the report output by HTMLTestRunner.'
)
runner.run(test_suite)
I am trying to run all the methods in both the classes in a single run. However, the code above did not do so. In the suite function, I tried to add multiple tests from the classes but that also did not work and was giving an error.
From this answer at the question "Is test suite deprecated in PyUnit?":
"unittest.TestSuite is not necessary if you want to run all the tests in a single module as unittest.main() will dynamically examine the module it is called from and find all classes that derive from unittest.TestCase."
There's more in that answer about when unittest.TestSuite is useful.
That said, I needed to make some changes to get these tests to work. Firstly, unittest looks for functions with "test_" at their start. Also, unittest's assertEqual and similar methods should be used, instead of just Python's assert statement. Doing that and eliminating some unneeded code led to:
import unittest
class TestClass1(unittest.TestCase):
def test_case1(self):
self.assertEqual(4, 3)
def test_case2(self):
self.assertEqual(4, 4)
class TestClass2(unittest.TestCase):
def test_case3(self):
self.assertEqual(1, 2)
unittest.main()
This produced appropriate output (3 tests run with 2 failures), which I won't reproduce here in the interest of space.

Categories