Getting Started with PyTest in Python
When you are working with a large project, you need to able to write unit tests for your code with ease and have increased readability for your test methods. Although the Python standard library allows a user to write unit tests using the unittest
framework, this framework is meant to be used at a smaller scale. It can lead to a lot of boilerplate code and does not have a lot of features that are required at scale when trying to publish a python package. For example, in one of my previous blog posts, I had to show how to execute python unit tests in parallel because unittest
does not natively support it.
The Python community often chooses to use third party testing frameworks, which require less boilerplate code and have useful features built in. The aim of this article is to introduce PyTest
package - as an alternative to unittest
framework - and give an example based approach on how to use it to write and execute unit tests in Python.
Installing pytest
To install this package, you need to have python
and its package manager pip
installed in your system. You can install pytest
with the following command
To verify that the installation worked, you can run the package in command line
If the package was installed successfully, the previous command will show you which pytest
version was installed. If you run into any issues during pytest
installation, you can leave a comment on this article.
Writing a Simple Test
Let us now write a simple test case to run a selenium test using pytest
. In this test case, we will conduct sanity testing on the Google Home page and verify that it displays a Link to “Gmail” web application. Create a new python module and name it test_gmail.py
.
The name of this module can be anything as long as it contains the “test” keyword in it.
Let us break down this pytest
test case.
- This module has a function called
test_gmail_link_visible
. This function can be discovered by thepytest
framework as a test as long as its name contains thetest_
prefix. You might have noticed that unlikeunittest
, we did not need need to create aTestClass
extendingunittest.TestCase
. This is the first part where the boilerplate code was reduced. - We instantiate a Chrome WebDriver using the selenium package and navigate to the Google home page using
driver.get()
. After this we find a link with test “Gmail” and verify the URL that it points to. - The verification step involved using Python’s native
assert
statement and thein
operator.pytest
reuses a lot of Python operators in asserts unlikeunittest
which has JUnit inspired legacy functions likeAssertEquals
.
Running a Test
You can run this test using the pytest
command line interface. To run all tests from all modules in current directory and its subdirectories you can execute the following command
To run a specific test method from a specific module
Test Fixtures
As with unittest
, we need a way to use test steps that are atomic in nature and can be easily reused across test cases. In unittest
, we specified the test fixture using setUp()
and tearDown()
methods. In pytest
, any method can be reused by making it a fixture. To create a fixture:
- Annotate the function with the
@pytest.fixture
decorator - Pass the fixture method name as a test method parameter
Since creating a driver and navigating to Google home page are steps that can be reused across tests, it can be useful to create fixtures for these.
In the above example, we created a fixture called driver
and used it value in the test. When this test is run, the fixture is run before and the return value is cached. Whenever driver
variable is requested within the test case for the first time, the value cached earlier is used.
Multiple Fixtures
Within the setUp()
method of unittest
framework, there can be multiple steps. If one of them fails, all the following steps might not execute leading to state that might cause failures in other test cases. This can be solved by declaring multiple fixtures for executing a single state altering task.
In the above example, we distributed driver creation and navigation into two separate fixtures. You might have noticed that the get_google
fixture is re-using the driver
fixture. This is exactly where the power of fixtures lies in. Fixtures can be built on top of each other.
Finalizing Fixtures
You might think that fixtures help can only help in re-using code that is run before test cases. But it can also be used to reuse steps after the test execution. This is analogous to the tearDown()
method of unittest
. To finalize a fixture, you can pause its execution, execute the test and then resume the fixture after test execution. This can be done using a very clever syntax using the yield
statement.
Let us update the driver
fixture to quit
the WebDriver object after the test finishes execution.
In this updated fixture, the execution will pause after the yield
statement. After the test finishes, the execution will resume and quit the WebDriver object.
Conclusion
This short guide shows you how to write a unit test using the pytest
package and invoke the test cases using the command line. It also shows you how to write test fixtures, reuse them and finalize them to conserve memory and do cleanup actions.
Lastly, if you have any questions on this blog post, you can reach out to us by joining our Discord server: