Tests that verify that a program works correctly under correct conditions are called positive tests. An exception raised during the positive testing lead to failure of the test.
Tests which check behaviour for invalid inputs are called negative tests. The purpose of the negative testing is verification of the graceful handling of error states. Raising of an exception is often the expected behaviour of the tested code.
For example, the computer_move function should raise an error
(e.g., ValueError) when the board is full.
It is much better to raise an exception than doing nothing and silently letting the program get stuck elsewhere. You can use such function in a more complex program and be sure that it will raise an understandable error when it is called under bad conditions. The error helps you to fix the actual problem. The sooner you discover an error the easier is to fix it.
Use the with statement and the raises function
to test that your code raises the expected exception.
The raises function is imported from the pytest module.
We have not talked about the with statement and context managers yet.
But don't worry, you will learn about them later. Just check how it is used
to test whether an exception is raised.
import pytest
import tic_tac_toe
def test_move_failure():
with pytest.raises(ValueError):
tic_tac_toe.computer_move('oxoxoxoxoxoxoxoxoxox')
Let's now try to edit the function for getting a perimeter of rectangle so that it raises a ValueError if any of the sides is smaller or equal to zero. Add a test for the new functionality.
Fixtures in pytest are reusable components that set up specific states (e.g. database connections, test data).
You can easily create and use a fixture in pytest in a following way:
import pytest
@pytest.fixture
def sample_data():
return [1, 2, 3, 4]
def test_sum(sample_data):
assert sum(sample_data) == 10
By default fixture is called once per a test function - scope=function parameter of a fixture.
For resource utilization purposes it could be useful to create a fixture only once per whole module:
import pytest
# A fixture to provide a configuration dictionary
@pytest.fixture(scope="module")
def config_data():
config = {
"api_endpoint": "http://api.example.com",
"retry_attempts": 5,
"timeout": 10
}
return config
# Example test using the fixture
def test_api_endpoint(config_data):
assert config_data["api_endpoint"] == "http://api.example.com"
# Another test using the same fixture
def test_retry_attempts(config_data):
assert config_data["retry_attempts"] == 5
This approach is useful when the setup does not involve external resources that require explicit cleanup after the tests.