Debug Pytest
If you can’t work out why a test is failing, you’re going to need to debug the test. In this chapter, we’ll be looking at some tips to help you do this.
Let’s look at the subtraction method from the Operators
class in the Calculator project:
def subtraction(self, operation):
self.operation = operation
self.sign = "-"
if self._is_operation_valid():
self._calculate_subtraction()
return self.result
And let’s check the scenario below:
def test_subtraction_operation():
sut = Operators()
operation = "5.5 + 10 - 10"
expected_value = 5.6
assert sut.subtraction(operation) == expected_value
After running the command to run the tests, I’ve noticed that the test has failed. If we want to get more details on the test, we can add some print()
instructions. So, I’m going to remove the assertion and insert some print()
instructions to get some more details on what the test is doing.
def test_subtraction_operation():
sut = Operators()
operation = "5.5 + 10 - 10"
expected_value = 5.6
sut.subtraction(operation)
print(f"operation : {sut.operation}")
print(f"sign : {sut.signe}")
print(f"result : {sut.result}")
print(f"expected value : {expected_value}")
But if you run the test again with the pytest
command, nothing is displayed. You need to add a -s
at the end so that the print()
instructions will display on your terminal, like so: pytest -s
.
You can also run the following command to simply run the scenario test_subtraction_operation
:
pytest -s tests/test_operators.py::test_subtraction_operation
If you have a number of tests in your file or project, this method can be really useful when debugging.
Using the print()
instructions that we added to the test, I now realize that I’m testing a subtraction operation, but the operation contains an addition operator.
So, we can now write:
def test_subtraction_operation():
sut = Operators()
operation = "5.5 - 10 - 10"
expected_value = -14.5
assert sut.subtraction(operation) == expected_value
This time, the test was successful!
Test Exceptions
Sometimes, you’ll need to check if an exception is raised correctly by the code. Fortunately, there’s a method that does just that!
Let’s consider a function that divides two numbers that are passed in as arguments:
def division(numerator, denominator):
return numerator / denominator
This code could raise an exception if it is being asked to divide by zero. This is why we’re not going to test that the code is supposed to fail, but that it will raise an exception in this specific case.
How do we do this?
By running a test with unexpected values and taking note of the exception raised. Pytest provides the raises(SomeException) method to specify that we expect the program to flag an error.
If we want to use it, we first need to import the pytest
module.
Add this to the top of your test file:
import pytest
Then we can write the following test:
def test_division():
numerator = 2
denominator = 0
with pytest.raises(ZeroDivisionError):
division(numerator, denominator)
Has the exception been correctly raised for you? Join me in the screencast below to find out more about testing exceptions.
Create Parametrized Tests
If you want to run a test with different datasets to check how the code behaves, look no further! You just need to use parametrized testing. Parametrized tests enable you to run the same scenario against multiple sets of sample data. This method means that you don’t need to duplicate your test to run it with different parameters.
Pytest provides a decorator that enables us to set up parametrized tests: @pytest.mark.parametrize
.
How do we use it?
Import the
pytest
module at the top of the test file.Define the parameter names in a character string with comma separators and place this string as the first argument of the decorator.
Provide the various parameters in a list of tuples and use this as the second argument of the decorator.
Define your test by placing each parameter name as an argument of the function.
Now we can write our first parametrized test:
import pytest
@pytest.mark.parametrize("number, square", [(2, 4), (10, 100), (16, 256)])
def test_should_return_square(number, square):
assert number ** 2 == square
Here’s a screencast showing a step-by-step guide to setting up this parametrized test:
Over to You!
Go back to the Calculator project and set up your first parametrized tests.
Find a suggested solution on the super-calculator project.
Let’s Recap!
Use the
-s
option when running thepytest
command to displayprint
instructions for your testing on the terminal: as followspytest -s
.To test an exception raised in the code, use the
raises()
function, provided by thepytest
module. If the section of code within thewith
statement doesn’t raise an exception, the test will fail.Parametrized tests enable you to run the same scenario using sample data. You can define your data sample using the
@pytest.mark.parametrize
decorator.
And that’s the end of part two, so now it’s time for the quiz! Your challenge is to get at least seven correct answers.