I have a test which currently runs with a single fixture like this:
#pytest.fixture()
def foo():
return 'foo'
def test_something(foo):
# assert something about foo
Now I am creating a slightly different fixture, say
#pytest.fixture
def bar():
return 'bar'
I need to repeat the exact same test against this second fixture. How can I do that without just copy/pasting the test and changing the parameter name?
Beside the test generation, you can do it the "fixture-way" for any number of sub-fixtures applied dynamically. For this, define the actual fixture to be used as a parameter:
#pytest.fixture
def arg(request):
return request.getfuncargvalue(request.param)
The define a test with am indirect parametrization (the param name arg and the fixture name arg must match):
#pytest.mark.parametrize('arg', ['foo', 'bar'], indirect=True)
def test_me(arg):
print(arg)
Lets also define those fixtures we refer to:
#pytest.fixture
def foo():
return 'foo'
#pytest.fixture
def bar():
return 'bar'
Observe how nicely parametrized and identified these tests are:
$ pytest test_me.py -s -v -ra
collected 2 items
test_me.py::test_me[foo] foo
PASSED
test_me.py::test_me[bar] bar
PASSED
Related
Let's say I'm doing some unit tests with pytest that need some configuration. And let's say that I also want to add some custom configuration, depending on the unit test I'm trying to create.
So, I have currently the following:
import pytest
def load_configuration(custom_config=None):
"""Loads some default configuration, and if necessary injects some custom configuration"""
config = some_complicated_stuff()
if custom_config:
config.update(custom_config)
return config
#pytest.fixture(scope="function")
def foo():
return 69
def test_bar_long_way(foo):
config = load_configuration(custom_config={"bar": 42})
assert foo == 69
assert config[bar] == 42
# do stuff with foo and config
Is there a way to inject that custom configuration into the unit test using a decorator (let's call it load_config), rather than having to create the configuration within the unit test itself? In this simplified example it's quite short, but in reality this takes a bit more space. I'm looking for a way to make it look like this:
#load_config({"bar": 42})
def test_bar_with_decorator(config, foo):
assert foo == 69
assert config["bar"] == 42
# do stuff with foo and config
I can't figure out how to create this load_config decorator. Any help would be appreciated :).
import pytest
def some_complicated_stuff():
return {"abc": 123}
def load_configuration(custom_config=None):
"""Loads some default configuration, and if necessary injects some custom configuration"""
config = some_complicated_stuff()
if custom_config:
config.update(custom_config)
return config
#pytest.mark.parametrize('config', [load_configuration({"bar": 42})])
def test_bar_long_way(config):
assert config["bar"] == 42
# do stuff with foo and config
parametrize is usually used for running the same test function but given different values for its parameters, but we can use it to run only once.
If you prefer having a nicer decorator :
def custom_config(config_val):
return pytest.mark.parametrize('config', [load_configuration(config_val)])
#custom_config({"bar": 42})
def test_bar_long_way(config):
assert config["bar"] == 42
I would like to write a Pytest fixture in conftest.py that, within itself, calls a function that, by default, does nothing but can be redefined in test modules.
#conftest.py
def within_foo():
pass
#pytest.fixture
def foo():
if some_condition():
within_foo()
something_else()
# test_foo.py
def within_foo():
print('this should be printed when running test_foo')
def test_foo(foo):
pass
Is there a clean way to implement this?
You can accomplish this with inheritance. Create a base class for all tests, than you can decide in which tests you want to override within_foo()
class BaseTest:
#pytest.fixture
def foo(self):
self.within_foo()
def within_foo(self):
pass
#pytest.mark.testing
class TestSomething(BaseTest):
def within_foo(self):
print('this should be printed when running test_foo')
def test_foo(self, foo):
pass
#pytest.mark.testing
class TestSomethingElse(BaseTest):
def test_foo_again(self, foo):
print('test_foo_again')
Outout
========================== 2 passed in 0.08 seconds ===========================
this should be printed when running test_foo
.test_foo
.test_foo_again
I have the following as conftest.py -->
def pytest_addoption(parser):
parser.addoption("--browser")
parser.addoption("--osType", help="Type of operating system")
parser.addoption("--hostURL", action="store", help="prod, stage, or dev")
#pytest.fixture(scope="session")
def browser(request):
return request.config.getoption("--browser")
#pytest.fixture(scope="session")
def osType(request):
return request.config.getoption("--osType")
#pytest.fixture(autouse=True)
def hostURL(request):
return request.config.getoption("--hostURL")
I would like to use the --hostURL flag to pass in value such as prod, stage or dev.
Here's how my test_TheMainTest.py looks -->
import unitest
import pytest
class CheckStatusCodeTest(unittest.TestCase, LoginPage, CustomSeleniumDriver):
def test_CheckStatusCodeOfPages(self, hostURL):
self.login(hostURL)
When I run the above test using pytest -q -s --hostURL prod I get the following error -->
TypeError: test_CheckStatusCodeOfCRPages() missing 1 required positional argument: 'hostURL'
Quoting the docs:
Note
unittest.TestCase methods cannot directly receive fixture arguments as implementing that is likely to inflict on the ability to run general unittest.TestCase test suites.
However, you can still pass regular fixture values to unittest-style tests using autouse fixtures:
class CheckStatusCodeTest(unittest.TestCase):
#pytest.fixture(autouse=True)
def _pass_fixture_value(self, hostURL):
self._hostURL = hostURL
def test_CheckStatusCodeOfPages(self):
assert self._hostURL
You can also check out this answer of mine tackling the same issue for more examples.
Another possibility is to implement an autouse fixture that modifies the test class explicitly. This is useful if you have lots of test classes that should have the identical setup:
#pytest.fixture(scope="class")
def set_fixture_value_to_class(request, hostURL):
request.cls._hostURL = hostURL
#pytest.mark.usefixtures('set_fixture_value_to_class')
class CheckStatusCodeTest(unittest.TestCase):
def test_CheckStatusCodeOfPages(self):
assert self._hostURL
#pytest.mark.usefixtures('set_fixture_value_to_class')
class AnotherTest(unittest.TestCase):
def test_spam(self):
assert self._hostURL
In this case, no need to copy the same fixture to each test class. Just mark all relevant test classes and you're good to go.
I am learning about parameterized tests with pyest. After following the relevant pytest documentation, I came up with this simple example:
import unittest
import pytest
#pytest.fixture(autouse=True, params=['foo', 'bar'])
def foo(request):
print('fixture')
print(request.param)
class Foo(unittest.TestCase):
def setUp(self):
print('unittest setUp()')
def test(self):
print('test')
This gives the following error:
Failed: The requested fixture has no parameter defined for the current test.
E
E Requested fixture 'foo' defined in:
E tests/fixture.py:7
Line 7 is def foo(request):.
What causes this error and how do I fix it?
The goal of fixtures is to pass objects to the test cases, but the fixture you've made doesn't return or yield anything.
Then I'm not sure you can pass objects to a unittest TestCase method, i think it may create some conflicts with the self parameter.
On the other side, it can work with a simple function :
#pytest.fixture(autouse=True, params=['foo', 'bar'])
def foo(request):
print('fixture')
print(request.param)
yield request.param
# class Foo(unittest.TestCase):
# def setUp(self):
# print('unittest setUp()')
#
# def _test(self):
# print('test')
def test_fixture(foo):
assert foo == 'foo'
>>> 1 failed, 1 passed in 0.05 seconds
# test 1 run with foo : OK
# test 2 run with bar : FAILED
EDIT :
Indeed : Why cant unittest.TestCases see my py.test fixtures?
I have a module with tests and I want to add some fixture parametrization in a conftest file. I don't want to mention this fixture in the test module, but I want this fixture to be available in the test_-method's self argument. How to do it?
In other words, how to make this code work?
conftest.py
def pytest_generate_tests (metafunc):
my_class = metafunc.function.__class__
metafunc.fixturenames.append('foo')
metafunc.parametrize('foo', [1, 2])
test.py
class TestFoo (object):
def test_foo (self):
assert self.foo > 0