π§© 4.4 Unit Testing#
π° Tutorial#
Unit testing is a crucial skill for software developers, helping ensure code quality and reliability. This module introduces you to unit testing in Python using the pytest framework, and explores the concept of test-driven development (TDD).
In this module, you will learn the fundamentals of unit testing in Python using the pytest framework. You will also explore test-driven development (TDD) and how it can be applied to real-world projects.
Explain the purpose of unit tests
Write unit tests for the light-mixing demo
Run and interpret unit tests to fix code
Explain test-driven development (TDD)
Purpose of Unit Tests#
Unit tests are small, isolated tests that validate the functionality of a specific section (unit) of your code, such as a function or a class. They help ensure that individual components of your program behave as expected, making debugging easier and reducing the chances of introducing bugs when making changes.
Key Benefits:#
Validate the correctness of your code.
Catch issues early in the development cycle.
Provide a safety net for refactoring.
Improve code quality and maintainability.
Example:
def add(a, b):
return a + b
A unit test for this function would check whether it correctly adds two numbers:
def test_add():
assert add(2, 3) == 5
Video Tutorial: What is Unit Testing?
Writing Unit Tests with pytest#
pytest is a popular Python testing framework that simplifies the process of writing and running tests. It automatically discovers test files and functions, and provides a clean, easy-to-use syntax.
Steps:#
Install pytest using pip:
pip install pytest
Create a test file (e.g.,
test_light_mixing.py) and define your unit tests inside it.Use the
assertstatement to check whether the function outputs match the expected results.
Example Unit Test for the Light-Mixing Demo:#
# light_mixing.py
def mix_colors(color1, color2):
if color1 == "red" and color2 == "blue":
return "purple"
elif color1 == "blue" and color2 == "yellow":
return "green"
elif color1 == "red" and color2 == "yellow":
return "orange"
else:
return "unknown"
# test_light_mixing.py
def test_mix_colors():
assert mix_colors("red", "blue") == "purple"
assert mix_colors("blue", "yellow") == "green"
assert mix_colors("red", "yellow") == "orange"
Running Tests:#
Navigate to the folder containing your test file.
Run pytest from the command line:
pytest
pytest will automatically discover all files starting with test_ and execute the tests.
Naming Conventions:#
Test files should be named
test_*.pyor*_test.pyTest functions should start with
test_Test classes should start with
Test
These naming conventions help pytest automatically discover your tests.
Video Tutorial: Introduction to pytest
Interpreting Test Results#
When you run pytest, it will display the results of your tests in the terminal. Hereβs how to interpret the output:
Green (PASSED): The test passed successfully.
Red (FAILED): The test failed. pytest will show you the expected result and the actual result so you can identify the issue.
Yellow (SKIPPED): The test was skipped (typically because of a specific condition, such as platform dependency).
Example Output:#
============================= test session starts ==============================
collected 3 items
test_light_mixing.py ... [100%]
============================== 3 passed in 0.03s ===============================
If a test fails, pytest will provide a detailed report:
def test_mix_colors():
> assert mix_colors("blue", "yellow") == "purple"
E AssertionError: assert 'green' == 'purple'
test_light_mixing.py:5: AssertionError
In this case, you can see that the test expected βpurpleβ but received βgreen,β indicating an issue with the mix_colors() function.
Code Coverage:#
pytest can be used with the pytest-cov plugin to measure code coverage, which indicates how much of your code is being tested:
pip install pytest-cov
pytest --cov=myproject tests/
This will show you the percentage of your code that is covered by tests, helping you identify areas that need more testing.
Video Tutorial: Understanding pytest Output
Debugging with pytest#
When a test fails, you can use pytestβs built-in debugging capabilities to help identify the issue. The --pdb flag can be used to drop into the Python debugger when a test fails, allowing you to inspect variables and step through the code.
Steps:#
Run pytest with the
--pdbflag:pytest --pdbWhen a test fails, pytest will drop into an interactive debugging session where you can inspect variables and explore the state of your program.
Example:#
(Pdb) print(color1)
'blue'
(Pdb) print(color2)
'yellow'
Video Tutorial: Debugging with pytest
Test-Driven Development (TDD)#
Test-driven development (TDD) is a software development approach where you write tests before writing the actual code. This ensures that the code you write is directly influenced by the tests, leading to a more robust and bug-free implementation.
TDD Workflow:#
Write a Test: Begin by writing a test for the new functionality you want to implement.
Run the Test: Since the functionality doesnβt exist yet, the test will fail.
Write Code: Write just enough code to make the test pass.
Run the Tests Again: Ensure all tests pass. If any tests fail, fix the code until they pass.
Refactor: Clean up the code while ensuring that the tests continue to pass.
Example of TDD for Light Mixing:#
Write the Test:
def test_mix_colors(): assert mix_colors("red", "blue") == "purple"
Run the Test: It will fail because the
mix_colors()function doesnβt exist yet.Write the Function:
def mix_colors(color1, color2): if color1 == "red" and color2 == "blue": return "purple" return "unknown"
Run the Test Again: The test should now pass.
Refactor the Code: Improve the
mix_colors()function while ensuring the test still passes.
Video Tutorial: Test-Driven Development with pytest