Can I override a fixture from a plugin in another plugin? - python

Lets assume I have:
pytest-3rd-party-plugin that provides a fixture cool_fixture
pytest-own-plugin that provides the fixture cool_fixture but with "better" functionality than cool_fixture
I want my own cool_fixture plugin to be the one that is selected. I know the following:
If I would have put that fixture in conftest.py, there wouldn't have been any problem, that would have been selected by the pytest runner. But I don't want to do that, as I am reusing this fixture in many projects.
I know that I can just call the fixture better_cool_fixture, and all will be fine :) But the fixture name is so pretty, so I kind of wanna keep it that way.
As I have understood pytest, there is no defined structure, what fixture that will be used. With exception of fixtures in module, conftest.py etc...
So anyway around this, so I don't need to change the name of this beautiful fixture?

Related

How to run a scoped function before all pytest fixtures of that scope?

I saw this question, asking the same about doing things before tests.
I need to do things before fixtures.
For example, I am setting up a dockerized environment, which I have to clean before building. To make things more complicated, I am using this plugin which defines fixtures I can't control or change, and I need something that comes before all fixtures (and does docker-compose down and other cleanup).
For example, when pytest starts, run the common per-session pre-step, then the fixtures, then the per-module pre-step, then the fixtures, and so on.
Is this a supported hook in pytest?
I couldn't find a relevant doc.
As #MrBean Bremen stated, pytest_sessionstart does the trick.
An annoying problem with that, is I can't use fixtures inside (naturally), with Argument(s) {'fixture_name'} are declared in the hookimpl but can not be found in the hookspec
I tried to fix that with pytest-dependency, but it doesn't seem to work on fixtures, only on tests.
I am left with the hacky workaround to extract required data for sessionstart to functions, and call them in sessionstart as well as in similarly named fixtures.
It works, but it is ugly.
Would accept a cleaner solution over this one

Many pytest fixtures vs. one large "container" fixture

We have a large python project which is tested using pytest, currently with unittest style classes, and we started migrating it to module-based, function style tests.
We are having a debate whether we should:
Split our large test base-class to many small, independent pytest fixtures; or
Maintain a one large fixture which lazily imports all other fixtures.
Pros for many fixtures:
Modular and probably easy to maintain
Each test only uses what it needs
Pros for one large fixture:
Less boilerplate code, each test only has one extra keyword arg
What should we do? Any opinions are welcome as long as they are explained. Thanks :)
The use of specific fixtures has a lot of advantages over the big one. Thanks to this pytest gained its popularity.
Different fixtures can reproduce various mutually exclusive states of the system under test. This is useful when you want to test various cases of your system behavior. Single fixture does not give such flexibility.
Pytest allows you to flexibly assemble a call to a fixture, when one fixture uses the results of the execution of another. Decomposition is an effective programming pattern and tests are no exception.
Fixtures in pytest can be parameterized, this is a very useful functionality, but its application will be impossible if you make one big fixture for all tests.
Conftest.py is a directory specific in pytest. So fixtures in pytest can be global (located in conftest), local (located inside the test module) and intermediate (located in conftest at the package level). This allows you to reuse common code, while not losing flexibility in specific cases.
Fixtures have scope (function, class, module, session), which gives additional flexibility.
The root idea of ​​the pytest framework is the use of fixtures at those levels where necessary. This is a big advantage over the xUnit style, but if you don’t use these advantages, the transition to pytest makes no sense.

Chain multiple fixtures pytest/unittest hybrid

I am working with a code base which which is mostly written with unittest. The tests are run with pytest. I am trying to simplify the tests by parameterizing some of them. To work towards this goal, I am trying to learn how to use pytest fixtures and refactor some of the existing unittest-based tests. The current tests have a global fixture in conftest.py:
#pytest.fixture(autouse=True)
def register_cleanup():
yield True
print("cleaning up database")
Now I want to add a fixture which is specific to one of my test modules, something along the lines of
#pytest.fixture()
def foo_fixture():
print("setup fixture")
yield
print("tear down fixture")
class Foo(unittest.TestCase):
def setUp(self):
print('unittest setUp()')
def test(self):
print('test')
However, the print() statements in this module fixture never execute. As an intermediate step, I am keeping the unittest-based structure and adding to it. Is it possible to get this working the way I want or do I need to scrap unittest altogether and go directly to pytest.
You'll want to mark it:
#pytest.mark.usefixtures("foo_fixture")
class Foo(unittest.TestCase):
...
Note: you may have more than one conftest.py. Put the more specific fixture into this test subdirectory's conftest.py.
Where would you expect this fixture to be used?
Did you want to turn on autouse=True here as well? In general, fixtures are only executed when they are a parameter to the test, and are less of generic cleanup mechanisms than scoped assumptions. By turning on autouse for this fixture you will essentially add an extra setup-cleanup step for the current module:
https://docs.pytest.org/en/latest/fixture.html#autouse-fixtures-xunit-setup-on-steroids
Here is how autouse fixtures work in other scopes:
autouse fixtures obey the scope= keyword-argument: if an autouse
fixture has scope='session' it will only be run once, no matter where
it is defined. scope='class' means it will be run once per class, etc.
if an autouse fixture is defined in a test module, all its test
functions automatically use it. if an autouse fixture is defined in a
conftest.py file then all tests in all test modules below its
directory will invoke the fixture. lastly, and please use that with
care: if you define an autouse fixture in a plugin, it will be invoked
for all tests in all projects where the plugin is installed. This can
be useful if a fixture only anyway works in the presence of certain
settings e. g. in the ini-file. Such a global fixture should always
quickly determine if it should do any work and avoid otherwise
expensive imports or computation.
[Adaptied comment into an answer]

Nosetest: add decorator for tests before execution

I use nosetests and allure framework for reporting purposes. In order to make the report look like I want, I have to add #nose.allure.feature('some feature') decorator to each test. The problem is that I have over 1000 test. Is there any way to modify tests before execution?
I was thinking about custom nose plugin, but not sure how can it be implemented.
A plug-in is definitely the way to go. there is a bunch of documentation on creating plug-ins:
http://nose.readthedocs.io/en/latest/doc_tests/test_issue119/empty_plugin.html
http://nose.readthedocs.io/en/latest/search.html?q=+plugin
I think you'll probably want to extend the test runner. You can look at what that decorator is doing before and after calling the function, and then just add that stuff in the test runner.
How about adding the decorator to the test classes instead?
Not sure if it will work, but sometimes works nicely for #patch.

In pytest, what is the use of conftest.py files?

I'm trying to understand what conftest.py files are meant to be used for.
In my (currently small) test suite I have one conftest.py file at the project root. I use it to define the fixtures that I inject into my tests.
I have two questions:
Is this the correct use of conftest.py? Does it have other uses?
Can I have more than one conftest.py file? When would I want to do that? Examples will be appreciated.
More generally, how would you define the purpose and correct use of conftest.py file(s) in a pytest test suite?
Is this the correct use of conftest.py?
Yes it is. Fixtures are a potential and common use of conftest.py. The
fixtures that you will define will be shared among all tests in your test suite. However, defining fixtures in the root conftest.py might be useless and it would slow down testing if such fixtures are not used by all tests.
Does it have other uses?
Yes it does.
Fixtures: Define fixtures for static data used by tests. This data can be accessed by all tests in the suite unless specified otherwise. This could be data as well as helpers of modules which will be passed to all tests.
External plugin loading: conftest.py is used to import external plugins or modules. By defining the following global variable, pytest will load the module and make it available for its test. Plugins are generally files defined in your project or other modules which might be needed in your tests. You can also load a set of predefined plugins as explained here.
pytest_plugins = "someapp.someplugin"
Hooks: You can specify hooks such as setup and teardown methods and much more to improve your tests. For a set of available hooks, read Hooks link. Example:
def pytest_runtest_setup(item):
""" called before ``pytest_runtest_call(item). """
#do some stuff`
Test root path: This is a bit of a hidden feature. By defining conftest.py in your root path, you will have pytest recognizing your application modules without specifying PYTHONPATH. In the background, py.test modifies your sys.path by including all submodules which are found from the root path.
Can I have more than one conftest.py file?
Yes you can and it is strongly recommended if your test structure is somewhat complex. conftest.py files have directory scope. Therefore, creating targeted fixtures and helpers is good practice.
When would I want to do that? Examples will be appreciated.
Several cases could fit:
Creating a set of tools or hooks for a particular group of tests.
root/mod/conftest.py
def pytest_runtest_setup(item):
print("I am mod")
#do some stuff
test root/mod2/test.py will NOT produce "I am mod"
Loading a set of fixtures for some tests but not for others.
root/mod/conftest.py
#pytest.fixture()
def fixture():
return "some stuff"
root/mod2/conftest.py
#pytest.fixture()
def fixture():
return "some other stuff"
root/mod2/test.py
def test(fixture):
print(fixture)
Will print "some other stuff".
Overriding hooks inherited from the root conftest.py.
root/mod/conftest.py
def pytest_runtest_setup(item):
print("I am mod")
#do some stuff
root/conftest.py
def pytest_runtest_setup(item):
print("I am root")
#do some stuff
By running any test inside root/mod, only "I am mod" is printed.
You can read more about conftest.py here.
EDIT:
What if I need plain-old helper functions to be called from a number
of tests in different modules - will they be available to me if I put
them in a conftest.py? Or should I simply put them in a helpers.py
module and import and use it in my test modules?
You can use conftest.py to define your helpers. However, you should follow common practice. Helpers can be used as fixtures at least in pytest. For example in my tests I have a mock redis helper which I inject into my tests this way.
root/helper/redis/redis.py
#pytest.fixture
def mock_redis():
return MockRedis()
root/tests/stuff/conftest.py
pytest_plugin="helper.redis.redis"
root/tests/stuff/test.py
def test(mock_redis):
print(mock_redis.get('stuff'))
This will be a test module that you can freely import in your tests. NOTE that you could potentially name redis.py as conftest.py if your module redis contains more tests. However, that practice is discouraged because of ambiguity.
If you want to use conftest.py, you can simply put that helper in your root conftest.py and inject it when needed.
root/tests/conftest.py
#pytest.fixture
def mock_redis():
return MockRedis()
root/tests/stuff/test.py
def test(mock_redis):
print(mock_redis.get(stuff))
Another thing you can do is to write an installable plugin. In that case your helper can be written anywhere but it needs to define an entry point to be installed in your and other potential test frameworks. See this.
If you don't want to use fixtures, you could of course define a simple helper and just use the plain old import wherever it is needed.
root/tests/helper/redis.py
class MockRedis():
# stuff
root/tests/stuff/test.py
from helper.redis import MockRedis
def test():
print(MockRedis().get(stuff))
However, here you might have problems with the path since the module is not in a child folder of the test. You should be able to overcome this (not tested) by adding an __init__.py to your helper
root/tests/helper/init.py
from .redis import MockRedis
Or simply adding the helper module to your PYTHONPATH.
In a wide meaning conftest.py is a local per-directory plugin. Here you define directory-specific hooks and fixtures. In my case a have a root directory containing project specific tests directories. Some common magic is stationed in 'root' conftest.py. Project specific - in their own ones. Can't see anything bad in storing fixtures in conftest.py unless they are not used widely (In that case I prefer to define them in test files directly)
I use the conftest.py file to define the fixtures that I inject into my tests, is this the correct use of conftest.py?
Yes, a fixture is usually used to get data ready for multiple tests.
Does it have other uses?
Yes, a fixture is a function that is run by pytest before, and sometimes
after, the actual test functions. The code in the fixture can do whatever you
want it to. For instance, a fixture can be used to get a data set for the tests to work on, or a fixture can also be used to get a system into a known state before running a test.
Can I have more than one conftest.py file? When would I want to do that?
First, it is possible to put fixtures into individual test files. However, to share fixtures among multiple test files, you need to use a conftest.py file somewhere centrally located for all of the tests. Fixtures can be shared by any test. They can be put in individual test files if you want the fixture to only be used by tests in that file.
Second, yes, you can have other conftest.py files in subdirectories of the top tests directory. If you do, fixtures defined in these lower-level conftest.py files will be available to tests in that directory and subdirectories.
Finally, putting fixtures in the conftest.py file at the test root will make them available in all test files.
Here are the official docs about using conftest.py to share fixtures:
conftest.py: sharing fixtures across multiple files
The conftest.py file serves as a means of providing fixtures for an entire directory. Fixtures defined in a conftest.py can be used by any test in that package without needing to import them (pytest will automatically discover them).
You can have multiple nested directories/packages containing your tests, and each directory can have its own conftest.py with its own fixtures, adding on to the ones provided by the conftest.py files in parent directories.

Categories