Pytest is a framework for running tests written in Python. It is easy to customize the launch of the tests. Let’s see what Pytest has to make it possible.
- possibility to create you own fixtures
- built-in fixtures
- tests parametrization for running same tests with different test data
- third-party plugins allow you to extend your tests capabilities:
- add graphical reporting
- empower API testing
- parallel tests launching
- and much more…
Pytest names requirements
You can organize the structure of the tests the way you want with almost no limitations. The only thing you should take care of, is the names of the directories, files, classes and methods/functions.
Directory name
All the directories that you want to include into the suite should have a name starting with “test”
File name
File name in the suite should start or end with “test”. So the wildcard for this name is “test_*.py” or “*_test.py”
Class name
The class name should start with “Test”
Method or function name
Every function and method that is supposed to test something should have a name that starts with “test”
Example of the suite is:
test_for_login/
test_signup.py
login_test.py
test_for_checkout/
test_cart.py
test_form.py
You can define your tests as class methods on as a separate functions in a file


Here is the example of the execution of the tests located in a class. Simple pytest run will not give you this picture. Use “pytest -v” to see more results like this:

How to run Pytest
In most cases locally you will run pytest from the Python virtual environment. You can read about virtual environment in this blog post how to create a virtual environment.
All tests
from the root directory of the project just run:
pytest
All tests from a file
pytest test_dir/test_file.py
All tests from a class
from a root directory of the project:
pytest test_dir/test_file.py::TestClass
One specific test
from a root directory of the project:
pytest test_dir/test_file.py::TestClass::test_one
Run Pytest without virtual environment
All the possibilities listed above are suitable. The difference is that you need to run pytest as a python module. In that case your machine’s Python environment will be used.
python -m pytest
Before / After blocks
There are no separate before / after blocks in pytest. This functionality can be implemented by fixtures. You can specify before / after what structural part of the tests it should be applied. This is configured using “scope” parameter. The options are “function”, “class”, “module”, “session”.
Here is the example of this before / after fixture on simple test:
import pytest
@pytest.fixture(scope='function')
def demo():
print('\nthis part will work before every test')
yield None
print('\nthis part will work after every test')
def test_one(demo):
assert 2 == 2
def test_two(demo):
assert 3 == 3
Note: to be able to see the text that you are printing during the test, run pytest with “-s” argument.
test_cases.py::test_one
this part will work before every test
PASSED
this part will work after every test
test_cases.py::test_two
this part will work before every test
PASSED
this part will work after every test
If you need the fixture to be applied to the test, you should specify its name in the test’s parameters. Here you see that fixture yields None but in most cases it will yield something that test needs. For example, connection:
import pytest
@pytest.fixture(scope='session')
def db_cursor():
db = mysql.connect(
host='my_db.rds.amazonaws.com',
port=3306,
user='username',
passwd='mypasswd',
database='college'
)
yield db.cursor(dictionary=True)
db.close()
def test_one(db_cursor):
db_cursor.execute('SELECT * FROM students')
students = db_cursor.fetchall()
assert len(students) > 0
def test_two(db_cursor):
db_cursor.execute('SELECT * FROM students WHERE id = 1')
student = db_cursor.fetchone()
assert student['name'] == ‘John’
def test_three():
assert 3 == 3
In this example you can see that I used “scope=’session’” for the fixture. So the connection will be established only once and it will happen before the testing session start. Database cursor will be yielded to each test that requires it. As you can see, the test_three doesn’t need the connection – so it doesn’t request it in the parameters. When the testing is finished, connection will be closed by the “db.close()” statement.
Pytest marks
To simplify the selection of the tests that should be run there is a possibility to mark them differently. So, later you will be able to specify what type of tests you want to run now. Do to that you can use this syntax:
import pytest
@pytest.mark.regression
def test_one():
assert 2 == 2
@pytest.mark.smoke
def test_two():
assert 3 == 3
And now you can run only the smoke tests from the suite:
pytest -v -s -m smoke
But before you run this you need to register these new marks so that pytest understand what these marks are. Otherwise you will see warnings at each run. On this page you can see all the options how to register your marks. Here is the example of one of them.
Create a file called “pytest.ini” with the following content:
[pytest]
markers =
smoke: smoke tests
regression: regression tests
And now you are ready to run any smoke and regression tests from you test suite.
Run same tests with different data
There are many situations when you need to run to test the same scenario but with different data. For example, testing login with different usernames/passwrods. To do that you can use pytest parametrize function. Here is the example:
import pytest
@pytest.mark.parametrize(
'num',
[2, 3, 4]
)
def test_one(num):
assert num == 2
This test_one will be executed three times for each data in the list. Of course, two of these three tests will fail.
test_cases.py::test_one[2] PASSED
test_cases.py::test_one[3] FAILED
test_cases.py::test_one[4] FAILED