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:
-
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 inunittest
. You can write simple test functions instead of creating classes and methods. -
Better Error Messages
pytest
provides more informative error messages compared tounittest
when assertions fail, making it easy to debug. -
Better Parametization
With
pytest
, you can run the same test with multiple sets of parameters. -
Pluggable Architecture
Pytest's plugin system allows extending its functionality. For example a plugin like
pytest-django
a can be used for testing thedjango
applications. -
Support for Assert Rewrite
pytest
rewritesassert
statements to provide more detailed failure messages, including variable inspection, which is somethingunittest
does not support directly without using specialized methods. -
Interoperability
And finally, you can use
pytest
to run tests written for other test frameworks such asunittest
andnose
.
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
.
- 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.
- 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.
- Integration test - Checks how different pieces of an application work together. These tests verify the interactions between components.
- 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:
- Create a directory called
app
. - 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