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
Related
I am testing a function with several incoming datasets defined as fixtures, but the fixture names get quite cumbersome to distinguish them from one another.
#pytest.fixture()
def dataset_with_foo():
pass
#pytest.fixture()
def dataset_with_bar():
pass
#pytest.fixture()
def dataset_with_foo_and_bar():
pass
def test_something(dataset_with_foo_and_bar):
pass
Is there a way to define some kind of alias for the option name to be shorter? For instance, something like:
#usesfixture("dataset_with_foo_and_bar", option_name="dataset")
def test_something(dataset):
pass
Create a super fixture and helper function to get desired fixture with one fixture.
import pytest
#pytest.fixture
def super_fixture(fixture1,fixture2,fixture3):
local_vars = locals()
def helper(fixture_name):
return local_vars.get(fixture_name)
return helper
def test_a(super_fixture):
# getting fixture1
assert super_fixture("fixture1")
Ok, the best way I could find to do it is by using deferred parametrized fixtures:
#pytest.fixture()
def dataset(request):
mapping = {
"with-foo": create_dataset_with_foo(),
"with-bar": create_dataset_with_bar(),
"with-foo-and-bar": create_dataset_with_foo_and_bar(),
}
return mapping[request.param]
def create_dataset_with_foo():
pass
def create_dataset_with_bar():
pass
def create_dataset_with_foo_and_bar():
pass
#pytest.mark.parametrize("dataset", ["with-foo"], indirect=True)
def test_something(dataset):
pass
#pytest.mark.parametrize("dataset", ["with-foo-and-bar"], indirect=True)
def test_something(dataset):
pass
There has been other attempts using pytest-lazy-fixture or specialized decorator, but I find it a bit too hacky..
https://gist.github.com/wcooley/7472b8de6edb1e8ceda560843c0519a8
As the doc "Where to patch" says, we need to patch where an object is looked up, (not where it's defined); so I understand that it's not possible to - let's say - create a reusable patch for a particular path
Imagine you have several modules importing an object you'd like to mock
# file_a.py
from foo.goo.hoo import settings
# file_b.py
from foo.goo.hoo import settings
# file_c.py
from foo.goo.hoo import settings
I was wondering if there is a way to create a decorator such as:
#mock_settings
def test_whatever(self, settings_mock):
...
instead of this solution:
#patch("some_module.file_a.settings")
def test_whatever(self, settings_mock):
...
#patch("some_module.file_b.settings")
def test_whatever(self, settings_mock):
...
#patch("some_module.file_c.settings")
def test_whatever(self, settings_mock):
...
As mentioned in the question, to patch an object you have to patch its reference in the module to be tested (in case it is imported using from ...import).
To have it patched in several modules, you can patch all of these modules with the same mock, and use that mock. If you know in advance which modules you want to patch, you can just do this. If you don't know them in advance, you have to try to patch the object in all loaded modules -- this may get a bit more complicated.
I will show an example using pytest and a pytest fixture, as this is more compact; you could wrap that in a decorator for usage in unittest, but that will not change the basics. Consider we have a class that needs to be mocked in several modules:
class_to_mock.py
class ClassToMock:
def foo(self, msg):
return msg
module1.py
from class_to_mock import ClassToMock
def do_something():
inst = ClassToMock()
return inst.foo("module1")
module2.py
from class_to_mock import ClassToMock
def do_something_else():
inst = ClassToMock()
return inst.foo("module2")
You can now write a fixture that mocks the class in all of these modules at once (here using pytest-mock for simplicity):
#pytest.fixture
def mocked_class(mocker):
mocked = Mock()
for module in ('module1', 'module2'):
mocker.patch(module + '.ClassToMock', mocked)
yield mocked
This can be used to test both modules:
def test_module1(mocked_class):
mocked_class.return_value.foo.return_value = 'mocked!'
assert module1.do_something() == 'mocked!'
def test_module2(mocked_class):
mocked_class.return_value.foo.return_value = 'mocked!'
assert module2.do_something_else() == 'mocked!'
If you want a generic version that mocks the class in all loaded modules, you can replace the fixture with something like this:
#pytest.fixture
def mocked_class(mocker):
mocked = Mock()
for name, module in list(sys.modules.items()):
if not inspect.ismodule(module):
continue
for cls_name, cls in module.__dict__.items():
try: # need that as inspect may raise for some modules
if inspect.isclass(cls) and cls_name == "ClassToMock":
mocker.patch(name + ".ClassToMock", mocked)
except Exception:
continue
yield mocked
This will work for this specific example - to generalize this, it has to consider more object types, the class shall be configurable, and there may be some more issues - opposed to the more simple version where you just enumerate the modules you want to patch, which will always work.
You could do something similar in unittest.setUp by putting the mock in an instance variable, though that is less elegant, because you are also responsible for stopping the mocking:
class ModulesTest(unittest.TestCase):
def setUp(self):
self.mocked_class = Mock()
self.mocks = []
for module in ('module1', 'module2'):
mocked = mock.patch(module + '.ClassToMock', self.mocked_class)
self.mocks.append(mocked)
mocked.start()
def tearDown(self):
for mocked in self.mocks:
mocked.stop()
def test_module1(self):
self.mocked_class.return_value.foo.return_value = 'mocked!'
assert module1.do_something() == 'mocked!'
And you can also wrap this in a decorator, to answer your original question at least partially:
def mocked_class_to_mock(f):
#wraps(f)
def _mocked_class_to_mock(*args, **kwargs):
mocked_class = Mock()
mocks = []
for module in ('module1', 'module2'):
mocked = mock.patch(module + '.ClassToMock', mocked_class)
mocks.append(mocked)
mocked.start()
kwargs['mocked_class'] = mocked_class # use a keyword arg for simplicity
f(*args, **kwargs)
for mocked in mocks:
mocked.stop()
return _mocked_class_to_mock
...
#mocked_class_to_mock
def test_module3(self, mocked_class):
mocked_class.return_value.foo.return_value = 'mocked!'
assert module3.do_something() == 'mocked!'
Of course, you can do the same with the more generic version, if needed.
Also note that I skipped the simpler case where the object is imported using import .... In this case, you have to patch the original module. In the generic fixture, you probably want to add that case always.
I have a python method like
import external_object
from external_lib1 import ExternalClass1
from external_lib2 import Hook
class MyClass(self):
def my_method(self):
ExternalClass.get('arg1') #should be mocked and return a specific value with this arg1
ExternalClass.get('arg2') #should be mocked and return a specific value with this arg2
def get_hook(self):
return Hook() # return a mock object with mocked method on it
def my_method(self):
object_1 = external_object.instance_type_1('args') # those are two different object instanciate from the same lib.
object_2 = external_object.instance_type_2('args')
object_1.method_1('arg') # should return what I want when object_1 mocked
object_2.method_2 ('arg') # should return what I want when object_2 mocked
In my test I would like to realise what I put in comments.
I could manage to do it, but every time it gets really messy.
I use to call flexmock for some stuff (by example ExternalClass.get('arg1') would be mock with a flexmock(ExternalClass).should_return('arg').with_args('arg') # etc...) but I'm tired of using different test libs to mock.
I would like to use only the mock library but I struggle to find a consistent way of doing it.
I like to use python's unittest lib. Concretely the unittest.mock which is a great lib to customize side effects and return value in unit tested functions.
They can be used as follows:
class Some(object):
"""
You want to test this class
external_lib is an external component we cannot test
"""
def __init__(self, external_lib):
self.lib = external_lib
def create_index(self, unique_index):
"""
Create an index.
"""
try:
self.lib.create(index=unique_index) # mock this
return True
except MyException as e:
self.logger.error(e.__dict__, color="red")
return False
class MockLib():
pass
class TestSome(unittest.TestCase):
def setUp(self):
self.lib = MockLib()
self.some = Some(self.lib)
def test_create_index(self):
# This will test the method returns True if everything went fine
self.some.create_index = MagicMock(return_value={})
self.assertTrue(self.some.create_index("test-index"))
def test_create_index_fail(self):
# This will test the exception is handled and return False
self.some.create_index = MagicMock(side_effect=MyException("error create"))
self.assertFalse(self.some.create_index("test-index"))
Put the TestSome() class file somewhere like your-codebase-path/tests and run:
python -m unittest -v
I hope it's useful.
I would like to avoid using the "test" prefix in classes and functions names and implement my own schema of the test parametrization.
I did the next code
test.py
import pytest
# class for inheritance to avoid "Test" prefix
class AtsClass:
__ATS_TEST_CLASS__ = True
# decorator to mark functions as tests (to avoid "Test" prefix)
def ats_test(f):
setattr(f, "__ATS_TEST_CLASS__", True)
return f
def test_1():
pass
#ats_test
def some_global_test():
pass
class MyClass(AtsClass):
def test_4(self):
pass
#ats_test
def some_func(self):
pass
conftest.py
import pytest
import inspect
# #pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makeitem(collector, name, obj):
# outcome = yield
# res = outcome.get_result()
if inspect.isclass(obj) and obj.__name__ != "AtsClass" and hasattr(obj, "__ATS_TEST_CLASS__") and obj.__ATS_TEST_CLASS__ == 1:
print("WE HAVE FOUND OUR CLASS")
return pytest.Class(name, parent=collector)
# outcome.force_result(pytest.Class(name, parent=collector))
if inspect.isfunction(obj) and hasattr(obj, "__ATS_TEST_CLASS__") and obj.__ATS_TEST_CLASS__ == 1:
print("WE HAVE FOUND OUR FUNCTION")
return pytest.Function(name, parent=collector)
# outcome.force_result([pytest.Function(name, parent=collector)])
def pytest_generate_tests(metafunc):
print("-->Generate: {}".format(metafunc.function.__name__))
In this case hook "pytest_pycollect_makeitem" creates test for function "some_global_test", but hook "pytest_generate_tests" is not executed for function "some_global_test".
I have found a solution, call collector._genfunctions(name, obj) from my hook. But I think it is not the right decision, cause _genfunctions is a private method and not declared.
Is there another way to solve my task?
So, nobody knows the answer and I decided to offer my solution (it can be useful for others):
class TestBaseClass:
__test__ = True
def mark_test(f):
setattr(f, "__test__", True)
return f
# using base class and decorator
class MyTestClass(TestBaseClass):
#mark_test
def some_func(self):
pass
Pytest uses attribute __test__ to detect nose-tests, so you can use nose-library or just use such base class and decorator.
If you want only to change prefix of tests you can set custom python_functionsand python_classes options at pytest.ini.
For more information follow a link.
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"