Using fixtures in Pytest - python

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

Related

How to get caller name inside pytest fixture?

Assume we have:
#pytest.fixture()
def setup():
print('All set up!')
return True
def foo(setup):
print('I am using a fixture to set things up')
setup_done=setup
I'm looking for a way to get to know caller function name (in this case: foo) from within setup fixture.
So far I have tried:
import inspect
#pytest.fixture()
def setup():
daddy_function_name = inspect.stack()[1][3]
print(daddy_function_name)
print('All set up!')
return True
But what gets printed is: call_fixture_func
How do I get foo from printing daddy_function_name?
You can use the built-in request fixture in your own fixture:
The request fixture is a special fixture providing information of the requesting test function.
Its node attribute is the
Underlying collection node (depends on current request scope).
import pytest
#pytest.fixture()
def setup(request):
return request.node.name
def test_foo(setup):
assert setup == "test_foo"

Change a way fixtures are called in pytest

I am having a fixture in conftest.py
#pytest.fixture(scope="function", autouse=True)
#pytest.mark.usefixtures
def pause_on_assert():
yield
if hasattr(sys, 'last_value') and isinstance(sys.last_value, AssertionError):
tkinter.messagebox.showinfo(sys.last_value)
Similarly there are many other fixures in the conftest.py with scope as session, module
My test cases looks like this
test.py
#pytest.fixture(scope="function", autouse=True)
def _wrapper:
print("pre condition")
yield
print("post condition")
def test_abc():
assert 1==0
Problem is I want my fixture that is there in conftest.py to run before yield of fixture that is in my testcase
How can i change order in the way fixture are executed
Here is an example of running my conftest.py function that prints "A" before my test function that prints "B".
cd to the parent directory, for this example it is py_tests and run.
pytest -s -v
The output is:
A
setting up
B
PASSED
With directory structure:
py_tests
-conftest.py
-tests
--tests.py
Files:
conftest.py
import pytest
#pytest.fixture(scope="function")
def print_one():
print("\n")
print("A")
test.py
import pytest
class Testonething:
#pytest.fixture(scope="function", autouse=True)
def setup(self, print_one):
print("setting up")
def test_one_thing(self):
print("B")
assert True
If you want to ensure a piece of code runs after the test function, but before all fixtures teardown, I'd advise to use the pytest_runtest_teardown hook instead. Replace the pause_on_assert fixture in your conftest.py with:
def pytest_runtest_teardown(item, nextitem):
if hasattr(sys, 'last_value') and isinstance(sys.last_value, AssertionError):
tkinter.messagebox.showinfo(sys.last_value)
Since your _wrapper is a function-scoped autouse fixture: it will be instantiated before other fixtures within the same scope. So, hot fix is to define _wrapper without autouse=True and try to call that decorator implicitly like:
def test_abc(_wrapper):
assert 1==0
Autouse source
[Update] If you don't have an ability to change your test suites, I suggest you just wipe all local specific _wrapper and refactor your conftest-specified fixture to call _wrapper, because fixture functions can use other fixtures themselves. Your conftest.py will be look like this:
# conftest.py
#pytest.fixture(scope="function", autouse=True)
def _wrapper(pause_on_assert):
print("pre condition")
yield
print("post condition")
#pytest.fixture()
def pause_on_assert():
yield
if hasattr(sys, 'last_value') and isinstance(sys.last_value, AssertionError):
tkinter.messagebox.showinfo(sys.last_value)
Modularity source

pytest fixture in setup_method

I have a function scoped fixture.
import pytest
#pytest.fixture(scope="function")
def some_fixture(req):
print "This is not the end"
return "okay"
Is it possible to invoke the fixture in the setup_method somehow.?
Something like the following snippet...
class TestOne(object):
def setup_method(self, method, some_fixture): # this would give argument error
print "okay"
def test_one(self):
pass
I know fixture would work same as setup_method, but I have got a very corner case scenario and I want the fixture to be executed in setup_method itself.
You can mark your setup_method as fixture & then call it in your tests.
class TestOne(object):
#pytest.fixture(scope="class")
def setup_method(self, method, some_fixture): # this would give argument error
print "okay"
def test_one(self, setup_method):
pass

Overriding sub-fixtures in pytest

I'm using pytest with some complicated dependency-injected fixtures. I have fixtures that use other fixtures in a long chain. I'd like to be able to alter some fixtures in the middle of the chain for specific tests.
Given these (simplified) fixtures:
#pytest.fixture
def cache():
return Cache()
# Use cache fixture in a new fixture.
#pytest.fixture
def resource(cache):
return Resource(cache=cache, working=True)
# Use resource fixture in a new fixture.
#pytest.fixture
def service(resource):
return Service(resource=resource)
And some tests:
def test_service_when_resource_working(service):
assert service.status == "good"
def test_service_when_resource_broken(service):
assert service.status == "bad"
How can I override the resource fixture so that it's like this:
#pytest.fixture
def broken_resource(cache):
return Resource(cache=cache, working=False)
...but only for the test_service_when_resource_broken test case? I can create a broken_service that uses broken_resource, but the reality is that the dependency chain is long, and I want to re-use all the fixtures, but selectively change some of them in the middle for selected tests.
I want to do something like this (pseudocode):
#pytest.override_fixture('resource', 'broken_resource')
def test_service_when_resource_broken(service):
# service should have been instantiated with broken_resource instead of resource.
assert service.status == "bad"
You can use markers on your tests to achieve what you are expecting.
Basically, you mark the test for which you need a different behaviour. In the fixture method look for that marker from the requesting test context and process.
Here is how you can do it.
#pytest.fixture
def cache():
return Cache()
# Use cache fixture in a new fixture.
#pytest.fixture
def resource(request, cache):
working = True
marker = request.node.get_marker("broken")
if marker:
working = False
return Resource(cache=cache, working=working)
# Use resource fixture in a new fixture.
#pytest.fixture
def service(resource):
return Service(resource=resource)
def test_service_when_resource_working(service):
assert service.status == "good"
#pytest.mark.broken
def test_service_when_resource_broken(service):
assert service.status == "bad"

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.
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.

Categories