Testing Python Code Using Pytest

pytest is a testing framework for Python applications that allows you to write small tests as well as complex ones easily. In this article, I’ll show you how to get it, how to run tests, and show you a few useful options you can use when working with it.

How to get it

Pytest is a cross platform library that works on Windows and Unix/Posix systems that have Python 2.7+, Python 3.4+, Jython or PyPy installed. The easiest way to get it is to install via pip in the commandline:

pip install - U pytest
Confirm that the install worked by running pytest --version in the commandline

Writing Tests

In my opinion, test functions written using pytest are simpler to write and easier to read compared to those written in unittest. Pytest uses Python’s assert statement to verify test expectations. Here is an example of a valid test written for pytest:

# test_example.py

def add_one(x):
    return x + 1

def test_add_one_returns_correct_result():
    assert add_one(3) == 4

The test function can now be executed.

Running tests

pytest can automatically discover files containing test code. To run tests, type pytest into the terminal. Running pytest this way without arguments causes it to first look for tests in the current directory. Pytest will run code contained in any file that follows this format

test_something.py
or
 something_test.py
. All the files that match the format will be collected and ran. From those files, it’ll collect test code from functions whose function definitions are prefixed with the word test, or classes whose names are prefixed with Test.

To run the test in the example above, you can do pytest or do pytest test_example.py Either method will work. It is also possible to change or customise how pytest discovers tests.

Expecting Exceptions

If you expect your code to raise an exception, you can test this out in pytest by using the raises helper:

import pytest

def f():
    raise SystemExit(1)

def test_f_function():
    with pytest.raises(SystemExit):
        f()    

In test_f_function(), the pytest.raises(SystemExit): statement says that whatever follows should raise a SystemExit exception. If an exception isn’t raised, the test fails. If a different Exception is raised, the test will fail.

Marking Tests

Tests can be marked or tagged using the pytest.mark helper. Marking allows you to add attributes or metadata to a test. You can create your own markers as well as use built in ones such as these:

  • skip — Always skip a test
  • skipif — Skip a test if a certain condition is met
  • xfail — Produce an expected failure if a certain condition is met
  • parametrize — perform multiple calls to the same test function

I’ll briefly discuss skipping tests here. To skip a test, mark it using the @pytest.mark.skip decorator and add a reason:

@pytest.mark.skip(reason="No way to test this right now")
def test_some_complex_function():
   ...

This technique is useful if you have a test you want included in the test suite but are not quite ready to run it yet.

There you have it, you now know how to get pytest, discover and run tests using it and how to skip some tests. In the next article, I’ll show you how to skip tests if a certain condition is true and also how test parametrization works. Thank you for reading.