Learn the very basics of creating tests in Python. Meet four types of software testing methods. Know the types of testing methods, and choose the most suitable ones for a specific context. Design tests and implement them in Python using the pytest and the unittest libraries.
- Why is testing so important?
- What is testing?
- Testing with pytest - a simple example
- Functon-based - searches for scripts and functions starting with
test_
- Functon-based - searches for scripts and functions starting with
- Context managers
- Example CLI run
- Keyword argument, by passing
-k "keyword_expression"
- Keyword argument, by passing
import pytest
# A function to test
def squared(number):
return number * number
# A test function always starts with "test"
def test_squared():
assert squared(-2) == squared(2)
# A test function
def test_raises():
with pytest.raises(ZeroDivisionError):
division(a=25, b=0)
> pytest func.py
- Test marker - a tag (a marker) of a test in the pytest library
- skip the test if the condition met
- test is expected to fail
# The skip marker example
@pytest.mark.skip
def test_get_len():
assert get_length('123') == 3
# The skipif marker example
@pytest.mark.skipif('2 * 2 == 5')
def test_get_len():
assert get_length('abc') == 3
# The xfail marker example
@pytest.mark.xfail
def test_gen_seq():
assert gen_sequence(-1)
- Fixture - a prepared environment that can be used for a test execution
- To make test setup easier
- To isolate the test of the environmental preparation
- To make the fixture code reusable
- Fixture Setup - preparing the environment and resources that are required by one or more tests
import pytest
# Fixture decorator
@pytest.fixture
# Fixture for data initialization
def data():
return [0, 1, 1, 2, 3, 5, 8, 13, 21]
def test_list(data):
assert len(data) == 9
assert 5 in data
- Chain fixtures requests - allows a fixture to use another fixture
- Establish dependencies between fixtures
- Keep the code modular
# Fixture that is requested by the other fixture
@pytest.fixture
def setup_data():
return "I am a fixture!"
# Fixture that is requested by the test function
@pytest.fixture
def process_data(setup_data):
return setup_data.upper()
- Autouse argument
- When
autouse=True
the fixture function is executing regardless of a request - Helps to reduce the number of redundant fixture calls, thus makes the code simpler
- When to use: in case we need to apply certain environment preparations or modifications
- When
# Autoused fixture
@pytest.fixture(autouse=True)
def set_pd_options():
pd.set_option('display.max_columns', 5000)
# Test function
def test_pd_options():
assert pd.get_option('display.max_columns') == 5000
- Fixture Teardown
- a process of cleaning up ("tearing down") resources that were allocated or created during the setup of a testing environment
- It is important to clean the environment at the end of a test
@pytest.fixture
def init_list():
return []
@pytest.fixture(autouse=True)
def add_numbers_to_list(init_list):
# Fixture Setup
init_list.extend([i for i in range(10)])
# Fixture output
yield init_list
# Teardown statement
init_list.clear()
def test_9(init_list):
assert 9 in init_list
- Unit testing - software testing method
- Foundation for testing "the bigger picture" of the software
- Unit - the smallest working part that can be tested
- Test case - a set of unit inputs and expected outputs
- Summarizes a particular piece of the problem
# Test Case 1: regular array
def test_regular():
assert sum_of_arr([1, 2, 3]) == 6
assert sum_of_arr([100, 150]) == 250
# Test Case 2: empty list
def test_empty():
assert sum_of_arr([]) == 0
# Test Case 3: one number
def test_one_number():
assert sum_of_arr([10]) == 10
assert sum_of_arr([0]) == 0
- Feature testing with
pytest
- A software system functionality, satisfies a particular user's requirement
- Are wider that units
- One unit does not work - does not mean the feature is NOT OK
- All units work as expected - does not mean the feature is OK
- Integration testing with
pytest
- Software testing method, that allows to verify that an interaction behaves normally
- Integration - an interaction between 2 or more modules inside of a system
- Performance testing with
pytest
- A type of testing that measures software performance
- Performance - how efficiently does software utilizes the resources of the system to accomplish a task
- Benchmark fixture
> pip install pytest-benchmark
> pytest Example_1.py
# Example_1.py
import time
def test_func(benchmark):
benchmark(time.sleep, 1)
# Example_2.py
def test_func(benchmark):
@benchmark
def sleep_for_1_sec():
time.sleep(1)
- Recap of OOP
unittest
- built-in Python framework for test automation- Based on OOP: each test case is a class, and each test is a method
- Declare a class inheriting from
unittest.TestCase
- Assertion methods
unittest -k
- run test methods and classes that match the pattern or substringunittest -f
- stop the test run on the first error or failureunittest -c
- Catch flag, lets to interrupt the test by pushing "Ctrl - C"unittest -v
- run tests with more detail
import unittest
# Declaring the TestCase class
class TestSquared(unittest.TestCase):
# Defining the test
def test_negative(self):
self.assertEqual((-3) ** 2, 9)
> python3 -m unittest test_sqneg.py
> python3 -m unittest -k "SomeStringOrPattern" test_script.py
> python3 -m unittest -f test_script.py
- Fixture in unittest - the preparaton needed to perform one or more tests
setUp()
method called to prepare the test fixture before the actual testtearDown()
method called after the test method to clean the environment
import unittest
class TestLi(unittest.TestCase):
# Fixture setup method
def setUp(self):
self.li = [i for i in range(100)]
# Fixture teardown method
def tearDown(self):
self.li.clear()
# Test method
def test_your_list(self):
self.assertIn(99, self.li)
self.assertNotIn(100, self.li)
- Practical examples