How To Use Pytest Conftest With Best Practices And Real Examples
Are you a python backend developer looking to improve your TDD (Test Driven Development) skills?
Or perhaps you heard or seen a colleague usepytest conftest
in a PR and didn’t understand why or how they used it?
Well, you’ve come to the right place.
While there’s a lot of documentation on how to use conftest
, the best practices using real repeatable examples are often lacking.
In this article, we’ll cover some basics about
- What are
Pytest Fixtures
and how to use them to improve your unit testing. - How are fixtures defined traditionally and their shortcomings?
- New ways to define and share fixtures across tests using
pytest conftest
- How to leverage external Pytest plugins with
conftest.py
So let’s begin.
What Are Pytest Fixtures?
As we’ll cover this in full detail in another article, I’ll keep this brief explaining only the key points you need, to understand conftest.py
.
To explain Pytest Fixtures let’s start with a real example.
The above repo contains code that calculates the area of 4 different planar shapes — Triangle
, Rectangle
, Square
and Circle
.
The class is initialised with 4 optional variables — radius
, side
, base
and height
.
Some of these could be the same but for this example let’s assume they are different.
core.py
1 | class AreaPlaneShapes: |
As you can see the above class contains 4 methods to calculate Area, along with an __init__
method.
Now let’s jump to the Unit Tests.
In the file test_core_fixtures.py we test the functional implementation of the various methods.
Now, we need to initialise the class AreaPlaneShapes()
somewhere.
A fixture is used to initialise test functions that can be repeatedly used across several test functions (unofficial definition).
This documentation from PyTest covers fixtures in detail.
How Are Pytest Fixtures Defined?
Fixtures are traditionally defined using the @pytest.fixture
decorator.
Out test_core_fixtures.py
unit test file has 3 fixtures.
area_fixture
area_fixture_side_none
area_fixture_height_none
test_core_fixtures.py
(Example Of Fixture)
1 |
|
test_core_fixtures.py
(Example Of Unit Test Using Fixture)
1 | def test_area_of_triangle(area_fixture) -> None: |
These are 3 independent Class initialisations that can be used across tests.
Note we’ve tested various functionality and error handling in the test file using the fixtures.
Without this, we’d have to initialise the AreaPlaneShapes()
class for each and every unit test.
So fixtures help us save time and write less code.
New Way To Share Fixtures — conftest.py
While this is great, do you see any problems with it?
Well, this is a very simple example.
Let’s say, instead of just 1 core.py
file, we have many nested classes.
This means in TDD terms, we’d have to write lots of Unit Tests.
For simplicity, we would split these into several test_*.py
files.
In our example, I’ve split the Unit Tests into 2 files — test_core_functionality.py
and test_core_error_handling.py
.
Each of these files test a different part of the source code.
So if we had 10 test files, we’d have to define Fixtures in each of them. There would be a lot of repetition.
There’s a better way, and that’s probably why you’re here.
The conftest.py
file will help us solve this. Luckily, pytest
and other testing frameworks are intelligent and will automatically pick up fixtures from conftest.py
.
In our example, you can see how we’ve defined 4 fixtures in conftest.py and used them extensively across all test files.
conftest.py
1 |
|
Unit Tests can now use the fixtures directly from conftest.py
test_core_error_handling.py
1 | def test_area_circle_none_radius(area_fixture_none_radius) -> None: |
This provides convenience and predictability, all while writing less code.
Other Uses of Conftest.py
Maybe you’re wondering… OK great, improved way of writing sharable fixtures. But what else can I do with conftest.py
Well, here are a few uses, succinctly specified in this Stack Overflow Post.
Load External PyTest Plugins
While most external plugins can be simply installed via pip
and automatically picked up by Pytest, you may want to include some custom packages.
In that case, including them in conftest.py
allows you to share them across your entire test suite.
For e.g.
pytest_plugins = ("myapp.testsupport.myplugin")
You can find an entire list of pytest plugins here.
An example of using PyTest Plugins pytest-pikachu
and pytest-progress
.
While there are other benefits of conftest for example - use in Hook Functions, it’s by far most used for Fixtures and Parameters.
We’ll cover more use cases of conftest in another article or a subsequent update.
Conclusion
This was a fairly short article but covers most of what you need to know about using conftest.py
to write more readable and efficient unit tests.
We covered an introduction on Pytest Fixtures including a real example of how they’re applied within the individual test file and also shared using conftest.py
.
I hope you appreciate the efficiency that conftest
provides in allowing you to share fixtures and parameters across your unit tests.
Lastly, we briefly looked at how to leverage Pytest External Plugins.
Please let me know if you’d like to learn something more specific about this topic or another and I’ll do my best to include them.
Till the next time… Cheers!