Testing is a very critical step in software development. It is the only way we can tell that our program works as expected under all known circumstances.

Python comes with some built-in testing frameworks, these are:

In this article we will learn about Pytest which is a robust framework for writing and running unit tests. Unlike unittest,  Pytest is not a standard library framework, this means that you will need to explicitly install it in your system.

You might wonder why we would need Pytest when we already have unittest freely available in the standard library. However, Pytest offers more powerful and versatile features than unittest and many other testing frameworks. Some of pytest's strengths are discussed below:

  1. Ease of Use and Simplicity

    pytest uses a more concise and readable syntax, eliminating the need to subclass or define a test class, as required in unittest. You can write simple test functions instead of creating classes and methods.

  2. Better Error Messages

    pytest provides more informative error messages compared to unittest when assertions fail, making it easy to debug.

  3. Better Parametization 

    With pytest, you can run the same test with multiple sets of parameters.

  4. Pluggable Architecture

    Pytest's plugin system allows extending its functionality. For example a plugin like pytest-django a can be used for testing the django applications.

  5. Support for Assert Rewrite 

    pytest rewrites assert statements to provide more detailed failure messages, including variable inspection, which is something unittest does not support directly without using specialized methods.

  6. Interoperability

    And finally, you can use pytest to run tests written for other test frameworks such as unittest and nose.

Test strategies Supported by Pytest

While pytest is commonly used for unit testing, it supports a wide range of testing  approaches. The following section shows some helpful definitions an various testing strategies supported by pytest.

  1. unit testing - Focuses on testing individual components (like functions or methods) in isolation from the rest of the application. This helps to ensure that small units of code behave as expected independently.
  2. Functional testing - Verifies that the software works as intended from the user's perspective, focusing on the output of a function or feature rather than the internal workings.
  3. Integration test - Checks how different pieces of an application work together. These tests verify the interactions between components.
  4. End-to-End (E2E) Testing - This is also known as system testing. These tests are usually more comprehensive and ensure that the entire system works as expected, as a whole.

Getting Pytest

As earlier mentioned, pytest is a third-party library and unlike unittest, it does not exist by default in Python's standard library,  you will have to install it.

The most direct way to install pytest is by running the following pip command in your system's command line:

pip install pytest
copy

The above command will install the latest version of pytest.

In some cases, you may want to install a specific version of pytest,  you can do so by specifying the required version in the pip command. In the following example we install pytest version 8.3.3

pip install pytest==8.3.3

 To test that the installation was successful, you can run the following command in the commandline.

pytest --help
copy

The above command will display helpful message  on using pytest. If you run the pytest command without any argument, pytest will traverse your current directory and all sub-directories looking for test files and will run existing test code.

Test Files and Functions

test files

If you run the pytest command without mentioning specific files, pytest will search the current directory and its sub-directories for all files with the following two formats:

*_test.py

test_*.py

Pytest will  automatically identify all files with the two formats as test files and will run them. We can also explicitly specify other file names.

test functions

Test functions can appear in test files or alongside other parts of the program. These are the functions that are actually run individually by pytest.

Pytest requires names of  test functions to have the following format:

test*

This means that the test functions should strictly begin with "test". Otherwise they will not be executed by test. Example of valid names for test functions:

test_area()

testArea()

testarea()

Your first pytest example

In this final section, we will look at a simple pytest example. 

You should perform the following actions:

  1. Create a directory called app.
  2. in the app directory create a file named test_math.py

In the test_math.py file, add the following code examples:

#test_math.py

import math

def test_sqrt():
   num = 25
   assert math.sqrt(num) == 5

def testsquare():
   num = 3
   assert num*num == 12 #fails

def testPower():
   assert math.pow(5, 3) == 125
copy

Now run the following pytest command in the app directory.

pytest -v
copy

The -v argument increases the verbosity of the test report, this means that the output will be more detailed. If you run the above command, the output will be as follows:

================================================= test session starts =================================================
platform win32 -- Python 3.12.3, pytest-8.3.3, pluggy-1.5.0 -- C:\Users\John\miniconda3\envs\env1\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\John\Desktop\app
collected 3 items

test_math.py::test_sqrt PASSED                                                                                   [ 33%]
test_math.py::testsquare FAILED                                                                                  [ 66%]
test_math.py::testPower PASSED                                                                                   [100%]

====================================================== FAILURES =======================================================
_____________________________________________________ testsquare ______________________________________________________

    def testsquare():
       num = 3
>      assert num*num == 12 #fails
E      assert (3 * 3) == 12

test_math.py:11: AssertionError
=============================================== short test summary info ===============================================
FAILED test_math.py::testsquare - assert (3 * 3) == 12
============================================= 1 failed, 2 passed in 0.16s =============================================
copy

 As clearly shown, the displayed output indicates that the second test function failed while the other two passed.

Note that running the pytest command without the -v flag will yield less detailed test report. 

pytest
copy

The output will be as shown below:

================================================= test session starts =================================================
platform win32 -- Python 3.12.3, pytest-8.3.3, pluggy-1.5.0
rootdir: C:\Users\John\Desktop\app
collected 3 items

test_math.py .F.                                                                                                 [100%]

====================================================== FAILURES =======================================================
_____________________________________________________ testsquare ______________________________________________________

    def testsquare():
       num = 3
>      assert num*num == 12 #fails
E      assert (3 * 3) == 12

test_math.py:11: AssertionError
=============================================== short test summary info ===============================================
FAILED test_math.py::testsquare - assert (3 * 3) == 12
============================================= 1 failed, 2 passed in 0.81s =============================================
copy