Is there a way to somehow use pytest fixtures from conftest.py in a test class's setup?
I need to initialize an object when the session starts and use it in some test classes' setup.
Something like this:
# conftest.py:
import pytest
#pytest.fixture(scope="session", autouse=True)
def myfixture(request):
return "myfixture"
# test_aaa.py
class TestAAA(object):
def setup(self, method, myfixture):
print("setup myfixture: {}".format(myfixture))
...
I don't think you can do it directly. However, you can decorate the whole class with pytest.mark.usefixtures, if that helps:
#pytest.mark.usefixtures(['myfixture'])
class TestAAA(object):
...
IIRC, setup_method will be called before any of the automatically applied fixtures.
You can also utilize autouse for class-level fixtures like so:
class TestAAA(object):
#pytest.fixture(autouse=True)
def init_aaa(self, myfixture):
...
I used this kind of a setup for a test class with pytest<=3.7.0 (it stopped working with pytest 3.7.1 release):
# conftest.py:
import pytest
# autouse=True does not work for fixtures that return value
# request param for fixture function is only required if the fixture uses it:
# e.g. for a teardown or parametrization. Otherwise don't use it.
#pytest.fixture(scope="session")
def myfixture():
return "myfixture"
# test_aaa.py
import pytest
class TestAAA(object):
#classmethod
#pytest.fixture(scope="class", autouse=True)
def setup(self, myfixture):
self.myfixture = myfixture
def test_function1(self):
# now you can use myfixture without passing it as param to each test function
assert self.myfixture == "myfixture"
For the example provided above, the object returned from the fixture can be set as an attribute of the test class. Any methods of this class will have access.
# test_fixture.py
import pytest
class TestAAA():
#pytest.fixture(scope="class", autouse=True)
def setup(self, myfixture):
TestAAA.myfixture = myfixture
def test_function1(self):
assert self.myfixture == "myfixture"
or if you inherit from unittest.Testcase you can do the following
# test_fixture.py
import pytest
from unittest import TestCase
class TestAAA(TestCase):
#pytest.fixture(scope="class", autouse=True)
def setup(self, myfixture):
self.myfixture = myfixture
def test_function1(self):
self.assertEqual(self.myfixture, "myfixture")
Related
I try to use fixtures in django.test.TestCase. It works correctly, but I do it for the first time. What is the best practice in this case?
from django.test import Client, TestCase
class SomeTestClass(TestCase):
fixtures = ['fixtures.json',]
#classmethod
def setUpClass(cls):
super().setUpClass()
cls.user = User.objects.get(id=1) # from fixtures
cls.authorized_client = Client()
cls.authorized_client.force_login(cls.user)
cls.someobject = Someclass.objects.get(id=1) # from fixtures
cls.anotherobject = Anotherclass.objects.get(id=1) # from fixtures
def test(self):
...
I am learning pytest and studying the behavior of different fixture scopes. I am seeing unexpected behavior of class scoped fixtures when I run the tests. This is my project structure.
Pytest_Basics
│ conftest.py
└───pack1
test_a.py
test_b.py
Here are contents of each file.
conftest.py
import pytest
#pytest.fixture(scope='session', autouse=True)
def ses_fix():
print('In session fixture')
#pytest.fixture(scope='package', autouse=True)
def pak_fix():
print('In package fixture')
#pytest.fixture(scope='module', autouse=True)
def mod_fix():
print('In module fixture')
#pytest.fixture(scope='class', autouse=True)
def cls_fix():
print('In class fixture')
#pytest.fixture(scope='function', autouse=True)
def func_fix():
print('In functon fixture')
test_a.py
class TestA:
def test_a1(self):
assert True
def test_a2(self):
assert True
def test_a3(self):
assert True
test_b.py
def test_b1():
assert True
def test_b2():
assert True
def test_b3():
assert True
When I run the test using pytest -v -s. I get below output.
pack1/test_a.py::TestA::test_a1 In session fixture
In package fixture
In module fixture
In class fixture
In functon fixture
PASSED
pack1/test_a.py::TestA::test_a2 In functon fixture
PASSED
pack1/test_a.py::TestA::test_a3 In functon fixture
PASSED
pack1/test_b.py::test_b1 In module fixture
In class fixture
In functon fixture
PASSED
pack1/test_b.py::test_b2 In class fixture
In functon fixture
PASSED
pack1/test_b.py::test_b3 In class fixture
In functon fixture
PASSED
I expected class scoped fixture will run only once as I have only one class in test_a.py module. However, I am seeing it is running while executing tests in test_b.py module.
What is causing this behavior? Is this a bug or I have limited understanding of class level fixtures.
Environment: Python - 3.9.5, Pytest - 6.2.4
This is indeed how class-scoped fixtures behave. It isn't directly mentioned in the documentation, but it may be inferred from it. As stated:
Fixtures are created when first requested by a test, and are destroyed based on their scope
Fixtures with autouse=True are applied to every test function in the session. They are destroyed based on their scope, meaning that
session-, module- or function-based fixtures are destroyed at the end of the session, module or function, where every test function lives. Class based fixtures, however, can be invoked outside of classes, and to be consistent, they must be destroyed after the function in this case - otherwise they could be never destroyed (if there are no classes), or would live across class boundaries. The important point is that class-scope (or any other scope) does not mean that the fixtures are applied only in that scope (e.g. inside a class), but to any test function, and the scope is only about the time it is destroyed.
For functions that do not live in a class, class-scoped fixtures behave just like function-scoped fixtures, but they are invoked before the function-scoped fixtures and shut down after the function-based fixtures:
conftest.py
#pytest.fixture(scope="function", autouse=True)
def fct_fixt():
print("function fixture start")
yield
print("function fixture end")
#pytest.fixture(scope="class", autouse=True)
def class_fixt():
print("class fixture start")
yield
print("class fixture end")
test_fixt.py
def test_1():
print("test_1 outside class")
class TestClass:
def test_1(self):
print("test_1 inside class")
def test_class_2(self):
print("test_2 inside class")
$ python -m pytest -s test_fixt.py
...
class fixture start
function fixture start
test_1 outside class
function fixture end
class fixture end
class fixture start
function fixture start
test_1 inside class
function fixture end
function fixture start
test_2 inside class
function fixture end
class fixture end
(indentation added for clarity)
I have a test class with few test methods and I want to patch some app classes and methods from the test methods.
In pytest docs I found an example of how to use monkeypatch module for tests. It that example all tests are just functions, not testclass methods.
But I have a class with test methods:
class MyTest(TestCase):
def setUp():
pass
def test_classmethod(self, monkeypatch):
# here I want to use monkeypatch.setattr()
pass
And just passing monkeypatch as method param is obviously doesn't work. So looks like py.test magic doesn't work this way.
So the question is simple and maybe stupid: how can I use monkeypatch.setattr() for pytest inside from the test class method?
It can't work in this form
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.
You might create monkeypatch directly
from _pytest.monkeypatch import MonkeyPatch
class MyTest(TestCase):
def setUp():
self.monkeypatch = MonkeyPatch()
def test_classmethod(self):
self.monkeypatch.setattr ...
...
or create own fixture, which will add monkeypatch to your class, and use #pytest.mark.usefixtures
#pytest.fixture(scope="class")
def monkeypatch_for_class(request):
request.cls.monkeypatch = MonkeyPatch()
#pytest.mark.usefixtures("monkeypatch_for_class")
class MyTest(TestCase):
def setUp():
pass
def test_classmethod(self):
self.monkeypatch.setattr ...
...
I had exactly the same problem.
This works perfectly
import unittest
import pandas as pd
from _pytest.monkeypatch import MonkeyPatch
from src.geipan_data import loadLongitudeLatitudeDateTestimony
class TestGeipanData(unittest.TestCase):
def setUp(self):
self.monkeypatch = MonkeyPatch()
def test_loadLongitudeLatitudeDateTestimony(self):
def read_csv(*args, **kwargs):
return pd.DataFrame({
'obs_date_heure': ['2010-05-21', '1926-05-21'],
'obs_1_lon': [45.123, 78.4564],
'obs_1_lat': [32.123, 98.4564],
})
self.monkeypatch.setattr(pd, 'read_csv', read_csv)
df = loadLongitudeLatitudeDateTestimony()
self.assertListEqual(
df.columns.values.tolist(),
['obs_date_heure', 'obs_1_lon', 'obs_1_lat']
)
In this example I do mock the pd.read_csv method with monkey patch and I uses asserListEqual that extends from unittest.TestCase
I want to mock all test methods of a TestCase.
My first try to use TestCase.setUp() did not work, since setUp() finishes before the test methods gets executed.
I can't mock the real test method with this inside setUp():
with mock.patch(...):
do_something()
I guess I am missing something.
How to use mock.patch() for all methods of a test case?
with mock.patch() is a context manager, the patch is unapplied when the context ends, and the context ends at the end of the block of code.
That means that the patches are unapplied again when setUp() ends.
Your options are to either use #mock.patch() as a class decorator or to use the start and stop methods on the patchers.
Using #mock.patch() as a class decorator has the same effect as applying it as a decorator to each and every test method:
#mock.patch('module.ClassName')
class TestFoo(unittest.TestCase):
def setUp(self):
# ...
def test_one(self, class_mock):
# ...
def test_two(self, class_mock):
# ...
Here both test_one and test_two are passed in a mock object because the #mock.patch() class decorator has found all test methods and decorated them.
Using the start and stop methods lets you apply and unapply patches in the setUp and tearDown methods:
class TestFoo(unittest.TestCase):
def setUp(self):
self.patch1 = mock.patch(...)
self.patch1.start()
def tearDown(self):
self.patch1.stop()
Here patch1 is started on set-up, and stopped again when the test is torn down. This acts just like the context manager, but instead hooks into the test boundaries.
Instead of using a tearDown, you can also register the patch.stop() as a cleanup function with TestCase.addCleanup():
class TestFoo(unittest.TestCase):
def setUp(self):
patch1 = mock.patch(...)
patch1.start()
self.addCleanup(patch1.stop)
You can use mock.patch.start, mock.patch.stop. (See patch methods: start and stop).
For example:
class MyTest(TestCase):
def setUp(self):
self.patcher = mock.patch('...')
self.MockClass = self.patcher.start()
def tearDown(self):
self.patcher.stop()
def test_something(self):
....
The most general solution, which works for all context managers is:
import unittest
class TCase(unittest.TestCase):
def setUp(self):
self.cm = mock.path(...)
self.cm.__enter__()
def test1(self):
...
def tearDown(self):
self.cm.__exit__(None, None, None)
I have tests like this:
import unittest
class TestBase(unittest.TestCase):
def setUp(self):
self.decorator = None
def testA(self):
data = someGeneratorA().generate()
self.assertTrue(self.decorator.someAction(data))
def testB(self):
data = someGeneratorB().generate()
self.assertTrue(self.decorator.someAction(data))
def testC(self):
data = someGeneratorC().generate()
self.assertTrue(self.decorator.someAction(data))
class TestCaseA(TestBase):
def setUp(self):
self.decorator = SomeDecoratorA
class TestCaseB(TestBase):
def setUp(self):
self.decorator = SomeDecoratorB
As you see, TestCaseA and TestCaseB is very similar, so I made TestBase class which implement body of testA, testB and testC method.
TestCaseA different from TestCaseB only decorator parameter.
So, I would like to ask, is any better way to organize my tests? And I have problem beacuse TestBase class - it's test's - shouldn't be runned ever (self.decorator is None so it will rase an exception)
Anything that inherits from unittest.TestCase is seen as a set of tests.
You could instead have your base class not inherit from TestCase, moving that base class to your concrete test classes instead:
class TestBase(object):
# base tests to be reused
class TestCaseA(TestBase, unittest.TestCase):
# Concrete tests, reusing tests defined on TestBase
class TestCaseB(TestBase, unittest.TestCase):
# Concrete tests, reusing tests defined on TestBase