How to Set Up and Run Your First Python Test With Pytest

anlene
8 Min Read

Testing is an essential part of software development. It helps catch bugs early and reduces the likelihood of errors down the line.


Pytest is one of the most popular testing frameworks for Python. It lets you write small and readable tests that can scale as your application grows. Learn how to set up and use Pytest with your Python code.

MAKEUSEOF VIDEOS OF THE DAYSCROLL TO CONTINUE WITH CONTENT

Setting Up Pytest

Before installing Pytest, it’s best to create a virtual environment to isolate your test environment, so you can avoid conflicts with other packages and dependencies.

To create a virtual environment, run the following command before installing Pytest.

 python -m venv tests

This will create a new virtual environment named tests in your current directory. To activate the environment, run this command if you’re on Linux or Mac:

 source tests/bin/activate

For Windows, run this command:

 tests\\Scripts\\activate

To install Pytest, you can use pip, the Python package manager, with this command in your terminal:

 pip install pytest

If you don’t have Pip, don’t worry; you can install Pip on Windows, Mac, and Linux.

Run the following command to check whether you installed Pytest correctly.

 pytest --version

This should return the installed version number.

Creating Your First Test

Consider the following function that adds two numbers and returns the result.

 def add_numbers(a, b):
    return a + b

Several things could go wrong with this function. For example, consider what happens if you call the function with non-numeric values ​​such as None or a value of type string. These are some of the potential edge cases that may cause the function to fail.

One of the first tests you write should check whether the function returns the expected result. To do this, you can use the assert keyword to compare the actual output of the function to the expected output. In the case of the add_numbers function, the test function might look like this:

 def test_add_numbers():
    assert add_numbers(2, 3) == 5
    assert add_numbers(-1, 1) == 0
    assert add_numbers(0, 0) == 0

This test function includes three assert statements, each of which compares the output of the add_numbers function to an expected value. The first test checks that add 2 and 3 returns 5, the second test checks that add -1 and 1 returns 0, and the third test checks that add 0 and 0 returns 0.

How to Run Tests With Pytest

After you have written your tests, the next step is to run them. To do this with Pytest, navigate to the directory containing your test file and run the pytest command:

 pytest

If everything works as expected, you will see a message indicating that all tests passed successfully. However, if any of the assertions fail, Pytest will report an error and show you the input values ​​that caused the failure.

For example, let’s say you run the following test function for the add_numbers function:

 def test_add_numbers():
    assert add_numbers(2, 3) == 6
    assert add_numbers(-1, 1) == 0
    assert add_numbers(0, 0) == 0

The first assertion will fail because the expected value is 6, but the actual value is 5 (the sum of 2 and 3). Pytest will return the following message:

The output of a pytest run showing a single test failing

This message shows you the input values ​​that caused the value and also tells you what the actual value should be. This makes it easy to quickly identify and fix errors in your code.

Using Pytest. raises to Assert Exceptions

Now, let’s write a test to cover one of the edge cases of the add_numbers function. When you pass a non-numeric argument like None to the function, Python should raise a TypeError exception.

You should already be handling exceptions in your Python programs, and you can test that your code raises them correctly too.

To do this, copy the following test function in your file. It uses the pytest. raises context manager to check if calling the add_number function with “None” raises a TypeError exception.

 import pytest

def test_add_numbers_with_invalid_inputs():
    with pytest.raises(TypeError):
        add_numbers(None, 2)

Then run Pytest from the command line. If the exception is not raised, the test will fail.

You can go further and check the details of the exception message. The context manager produces an ExceptionInfo object with the details.

For example, in this test function, assert the exception message like this:

 def test_add_numbers_with_invalid_inputs():
    with pytest.raises(TypeError) as exc_info:
        add_numbers(None, 2)

    assert exc_info.value.args[0] == "unsupported operand type(s) for +: 'NoneType' and 'int'"

If the message does not match the one in the test, Pytest will indicate a failure.

How to Use Parameterized Testing to Test Multiple Inputs At Once

Instead of manually calling a function with multiple inputs like this:

 def test_add_numbers():
    assert add_numbers(2, 3) == 6
    assert add_numbers(-1, 1) == 0
    assert add_numbers(0, 0) == 0

Pytest provides a parameterized testing feature that allows you to do the same thing more easily. Here’s how you can rewrite the test function above:

 import pytest

@pytest.mark.parametrize("a,b,expected", [
    (2, 3, 5),
    (-1, 1, 0),
    (0, 0, 0)
])
def test_add_numbers(a, b, expected):
    assert add_numbers(a, b) == expected

How to Run Multiple Tests

So far, you’ve only written two tests for the add_numbers function. For more complex functions with more tests, you may want to group them in a class.

For example, here is how you would create a test class for the add function.

 class TestAddFunction:
@pytest.mark.parametrize("a, b, expected", [
        (2, 3, 5),
        (-1, 1, 0),
        (0, 0, 0),
    ])
    def test_addition_with_numbers(self, a, b, expected):
        assert add_numbers(a, b) == expected

    def test_add_numbers_with_invalid_inputs(self):
        with pytest.raises(TypeError) as exc_info:
            add_numbers(None, 2)
        assert exc_info.value.args[0] == "unsupported operand type(s) for +: 'NoneType' and 'int'"

Note that you need to prefix the class name with “Test” so that Pytest can identify it as a test class and run it.

Pytest Has Many More Features

Using Pytest, you can automatically verify your code works as you expect. Pytest offers many other features such as fixtures that allow you to set up and tear down test data and marks for setting up metadata for your test functions.

Plus, you can integrate Pytest in your CI pipeline and start running tests automatically and continuously when you change your code.

Share this Article