I use the following to mock constant values for a test with py.test:
#patch('ConstantsModule.ConstantsClass.DELAY_TIME', 10)
def test_PowerUp():
...
thing = Thing.Thing()
assert thing.a == 1
This mocks DELAY_TIME as used in both the test, and in Thing, which is what I expected.
I wanted to do this for all the tests in this file, so I tried
#patch('ConstantsModule.ConstantsClass.DELAY_TIME', 10)
#pytest.fixture(autouse=True)
def NoDelay():
pass
But that doesn't seem to have the same effect.
Here is a similar question: pytest-mock mocker in pytest fixture, but the mock seems done in a non-decorator way there.
I'd say patching via decorator is not the optimal approach here. I'd use the context manager:
import pytest
from unittest.mock import patch
#pytest.fixture(autouse=True)
def no_delay():
with patch('ConstantsModule.ConstantsClass.DELAY_TIME', 10):
yield
This way, patching is cleanly reverted on test teardown.
pytest provides builtin patching support via the monkeypatch fixture. So to patch the constant for all tests in the file you can create the following autouse fixture:
#pytest.fixture(autouse=True)
def no_delay(monkeypatch):
monkeypatch.setattr(ConstantsModule.ConstantsClass, 'DELAY_TIME', 10)
If you don't want to have the ConstantsModule imported in your tests you can use a string, see the full API reference.
Related
I am writing tests using Pytest and Python 3.6. My tests use an ENV var called online, which I use to determine whether the tests will hit the real web service or whether it will just be playback.
I want to patch sleep when the tests are not online for all the tests. When the tests are in online mode, dont patch. Is there a way to do this on pytest? My initial thought was to use a fixture that uses the env var to monkeypatch or not.
I am not sure how to go about with this?
The easiest way to do this is to use an autouse fixture. The standard way is to use mock.patch to mock the function:
from unittest import mock
import pytest
#pytest.fixture(autouse=True)
def patch_sleep():
if "ONLINE" in os.environ and os.environ["ONLINE"] == "1":
with mock.patch("time.sleep"):
yield
else:
yield
In this case the function will be patched in each test, and the patch reverted after each test. If you use autouse=True, the fixture will be invoked automatically in all tests, otherwise you have to reference it in the tests.
If you know that your environment cannot change during the test, you can use a session-scoped fixture that is invoked only once in the test session:
#pytest.fixture(autouse=True, scope="session")
def patch_sleep():
...
In this case, time.sleep will be patched before all tests run, and the patch reverted after all tests have finished.
If you don't want to use unittest.patch, you can use the pytest-mock plugin, which provides the mocker fixture:
#pytest.fixture(autouse=True)
def patch_sleep(mocker):
if "ONLINE" in os.environ and os.environ["ONLINE"] == "1":
mocker.patch("time.sleep")
yield
else:
yield
You can also use monkeypatch, though that is not the usual way for patching a function:
#pytest.fixture(autouse=True)
def patch_sleep(monkeypatch):
def dont_sleep(sec):
pass
if "ONLINE" in os.environ and os.environ["ONLINE"] == "1":
monkeypatch.setattr(time, "sleep", dont_sleep)
yield
else:
yield
Is it possible to prevent the execution of "function scoped" fixtures with autouse=True on specific marks only?
I have the following fixture set to autouse so that all outgoing requests are automatically mocked out:
#pytest.fixture(autouse=True)
def no_requests(monkeypatch):
monkeypatch.setattr("requests.sessions.Session.request", MagicMock())
But I have a mark called endtoend that I use to define a series of tests that are allowed to make external requests for more robust end to end testing. I would like to inject no_requests in all tests (the vast majority), but not in tests like the following:
#pytest.mark.endtoend
def test_api_returns_ok():
assert make_request().status_code == 200
Is this possible?
You can also use the request object in your fixture to check the markers used on the test, and don't do anything if a specific marker is set:
import pytest
#pytest.fixture(autouse=True)
def autofixt(request):
if 'noautofixt' in request.keywords:
return
print("patching stuff")
def test1():
pass
#pytest.mark.noautofixt
def test2():
pass
Output with -vs:
x.py::test1 patching stuff
PASSED
x.py::test2 PASSED
In case you have your endtoend tests in specific modules or classes you could also just override the no_requests fixture locally, for example assuming you group all your integration tests in a file called end_to_end.py:
# test_end_to_end.py
#pytest.fixture(autouse=True)
def no_requests():
return
def test_api_returns_ok():
# Should make a real request.
assert make_request().status_code == 200
I wasn't able to find a way to disable fixtures with autouse=True, but I did find a way to revert the changes made in my no_requests fixture. monkeypatch has a method undo that reverts all patches made on the stack, so I was able to call it in my endtoend tests like so:
#pytest.mark.endtoend
def test_api_returns_ok(monkeypatch):
monkeypatch.undo()
assert make_request().status_code == 200
It would be difficult and probably not possible to cancel or change the autouse
You can't canel an autouse, being as it's autouse. Maybe you could do something to change the autouse fixture based on a mark's condition. But this would be hackish and difficult.
possibly with:
import pytest
from _pytest.mark import MarkInfo
I couldn't find a way to do this, but maybe the #pytest.fixture(autouse=True) could get the MarkInfo and if it came back 'endtoend' the fixture wouldn't set the attribute. But you would also have to set a condition in the fixture parameters.
i.e.: #pytest.fixture(True=MarkInfo, autouse=True). Something like that. But I couldn't find a way.
It's recommended that you organize tests to prevent this
You could just separate the no_requests from the endtoend tests by either:
limit the scope of your autouse fixture
put the no_requests into a class
Not make it an auto use, just pass it into the params of each def you need it
Like so:
class NoRequests:
#pytest.fixture(scope='module', autouse=True)
def no_requests(monkeypatch):
monkeypatch.setattr("requests.sessions.Session.request", MagicMock())
def test_no_request1(self):
# do stuff here
# and so on
This is good practice. Maybe a different organization could help
But in your case, it's probably easiest to monkeypatch.undo()
Can one fixture build on another in pytest? I have a very simple fixture called "cleaner" defined as:
import pytest
from mypackage import db
#pytest.fixture()
def cleaner(request):
def finalizer():
db.clear()
request.addfinalizer(finalizer)
then in my setup.cfg I have:
[pytest]
norecursedirs = .git venv
usefixtures = cleaner
This results in the database being truncated after each test, which is great. But now I want other fixtures I make to also call the finalizer from cleaner. Is there a way to define another fixture that somehow expands on or calls cleaner?
You have to declare that your other fixture depends on cleaner explicitly:
import pytest
#pytest.fixture
def cleaner(request):
def finalizer():
print '\n"cleaner" finalized'
print '\n"cleaner" fixture'
request.addfinalizer(finalizer)
#pytest.fixture
def other(cleaner):
print '\n"other" fixture'
def test_foo(other):
pass
Running this with py.test -s -v produces:
test_foo.py#16::test_foo
"cleaner" fixture
"other" fixture
PASSED
"cleaner" finalized
I have encountered something mysterious, when using patch decorator from mock package integrated with pytest fixture.
I have two modules:
-----test folder
-------func.py
-------test_test.py
in func.py:
def a():
return 1
def b():
return a()
in test_test.py:
import pytest
from func import a,b
from mock import patch,Mock
#pytest.fixture(scope="module")
def brands():
return 1
mock_b=Mock()
#patch('test_test.b',mock_b)
def test_compute_scores(brands):
a()
It seems that patch decorate is not compatible with pytest fixture. Does anyone have a insight on that? Thanks
When using pytest fixture with mock.patch, test parameter order is crucial.
If you place a fixture parameter before a mocked one:
from unittest import mock
#mock.patch('my.module.my.class')
def test_my_code(my_fixture, mocked_class):
then the mock object will be in my_fixture and mocked_class will be search as a fixture:
fixture 'mocked_class' not found
But, if you reverse the order, placing the fixture parameter at the end:
from unittest import mock
#mock.patch('my.module.my.class')
def test_my_code(mocked_class, my_fixture):
then all will be fine.
As of Python3.3, the mock module has been pulled into the unittest library. There is also a backport (for previous versions of Python) available as the standalone library mock.
Combining these 2 libraries within the same test-suite yields the above-mentioned error:
E fixture 'fixture_name' not found
Within your test-suite's virtual environment, run pip uninstall mock, and make sure you aren't using the backported library alongside the core unittest library. When you re-run your tests after uninstalling, you would see ImportErrors if this were the case.
Replace all instances of this import with from unittest.mock import <stuff>.
Hopefully this answer on an old question will help someone.
First off, the question doesn't include the error, so we don't really know what's up. But I'll try to provide something that helped me.
If you want a test decorated with a patched object, then in order for it to work with pytest you could just do this:
#mock.patch('mocked.module')
def test_me(*args):
mocked_module = args[0]
Or for multiple patches:
#mock.patch('mocked.module1')
#mock.patch('mocked.module')
def test_me(*args):
mocked_module1, mocked_module2 = args
pytest is looking for the names of the fixtures to look up in the test function/method. Providing the *args argument gives us a good workaround the lookup phase. So, to include a fixture with patches, you could do this:
# from question
#pytest.fixture(scope="module")
def brands():
return 1
#mock.patch('mocked.module1')
def test_me(brands, *args):
mocked_module1 = args[0]
This worked for me running python 3.6 and pytest 3.0.6.
If you have multiple patches to be applied, order they are injected is important:
# from question
#pytest.fixture(scope="module")
def brands():
return 1
# notice the order
#patch('my.module.my.class1')
#patch('my.module.my.class2')
def test_list_instance_elb_tg(mocked_class2, mocked_class1, brands):
pass
This doesn't address your question directly, but there is the pytest-mock plugin which allows you to write this instead:
def test_compute_scores(brands, mock):
mock_b = mock.patch('test_test.b')
a()
a) For me the solution was to use a with block inside the test function instead of using a #patch decoration before the test function:
class TestFoo:
def test_baa(self, my_fixture):
with patch(
'module.Class.function_to_patch',
MagicMock(return_value='mocked_result')
) as mocked_function_to_patch:
result= my_fixture.baa('mocked_input')
assert result == 'mocked_result'
mocked_function_to_patch.assert_has_calls([
call('mocked_input')
])
This solution does work inside classes (that are used to structure/group my test methods). Using the with block, you don't need to worry about the order of the arguments. I find it more explicit then the injection mechanism but the code becomes ugly if you patch more then one variable. If you need to patch many dependencies, that might be a signal that your tested function does too many things and that you should refactor it, e.g. by extracting some of the functionality to extra functions.
b) If you are outside classes and do want a patched object to be injected as extra argument in a test method... please note that #patch does not support to define the mock as second argument of the decoration:
#patch('path.to.foo', MagicMock(return_value='foo_value'))
def test_baa(self, my_fixture, mocked_foo):
does not work.
=> Make sure to pass the path as only argument to the decoration. Then define the return value inside the test function:
#patch('path.to.foo')
def test_baa(self, my_fixture, mocked_foo):
mocked_foo.return_value = 'foo_value'
(Unfortunately, this does not seem to work inside classes.)
First let inject the fixture(s), then let inject the variables of the #patch decorations (e.g. 'mocked_foo').
The name of the injected fixture 'my_fixture' needs to be correct. It needs to match the name of the decorated fixture function (or the explicit name used in the fixture decoration).
The name of the injected patch variable 'mocked_foo' does not follow a distinct naming pattern. You can choose it as you like, independent from the corresponding path of the #patch decoration.
If you inject several patched variables, note that the order is reversed: the mocked instance belonging to the last #patch decoration is injected first:
#patch('path.to.foo')
#patch('path.to.qux')
def test_baa(self, my_fixture, mocked_qux, mocked_foo):
mocked_foo.return_value = 'foo_value'
I had the same problem and solution for me was to use mock library in 1.0.1 version (before I was using unittest.mock in 2.6.0 version). Now it works like a charm :)
I'm trying to use py.test's fixtures with my unit tests, in conjunction with unittest. I've put several fixtures in a conftest.py file at the top level of the project (as described here), decorated them with #pytest.fixture, and put their names as arguments to the test functions that require them.
The fixtures are registering correctly, as shown by py.test --fixtures test_stuff.py, but when I run py.test, I get NameError: global name 'my_fixture' is not defined. This appears to only occur when I use subclasses of unittest.TestCase—but the py.test docs seem to say that it plays well with unittest.
Why can't the tests see the fixtures when I use unittest.TestCase?
Doesn't work:
conftest.py
#pytest.fixture
def my_fixture():
return 'This is some fixture data'
test_stuff.py
import unittest
import pytest
class TestWithFixtures(unittest.TestCase):
def test_with_a_fixture(self, my_fixture):
print(my_fixture)
Works:
conftest.py
#pytest.fixture()
def my_fixture():
return 'This is some fixture data'
test_stuff.py
import pytest
class TestWithFixtures:
def test_with_a_fixture(self, my_fixture):
print(my_fixture)
I'm asking this question more out of curiosity; for now I'm just ditching unittest altogether.
While pytest supports receiving fixtures via test function arguments
for non-unittest test methods, unittest.TestCase methods cannot
directly receive fixture function arguments as implementing that is
likely to inflict on the ability to run general unittest.TestCase test
suites.
From the note section at the bottom of:
https://pytest.org/en/latest/unittest.html
It's possible to use fixtures with unittest.TestCasees. See that page for more information.
You can use the pytest fixtures in unittest.TestCase with the pytest option autouse. However, if you use the test_ for the unit method using the fixture the following error will appear:
Fixtures are not meant to be called directly,...
### conftest.py
#pytest.fixture
def my_fixture():
return 'This is some fixture data'
One solution is to use a prepare_fixture method to set fixtures as an attribute of the TestWithFixtures class, so that fixtures are available to all unit test methods.
### test_stuff.py
import unittest
import pytest
class TestWithFixtures(unittest.TestCase):
#pytest.fixture(autouse=True)
def prepare_fixture(self, my_fixture):
self.myfixture = my_fixture
def test_with_a_fixture(self):
print(self.myfixture)
define the fixture as an accessible variable, (like input in the following example). To define it, use request.cls.VARIABLE_NAME_YOU_DEFINE = RETURN_VALUE
use #pytest.mark.usefixtures("YOUR_FIXTURE") to use fixture outside of the unittest class, inside the unittest class, access the fixture by self.VARIABLE_NAME_YOU_DEFINE.
e.g.
import unittest
import pytest
#pytest.fixture(scope="class")
def test_input(request):
request.cls.input = {"key": "value"}
#pytest.mark.usefixtures("test_input")
class MyTestCase(unittest.TestCase):
def test_something(self):
self.assertEqual(self.input["key"], "value")