🧩 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.

  1. Explain the purpose of unit tests

  2. Write unit tests for the light-mixing demo

  3. Run and interpret unit tests to fix code

  4. 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:#

  1. Install pytest using pip:

    pip install pytest
    
  2. Create a test file (e.g., test_light_mixing.py) and define your unit tests inside it.

  3. Use the assert statement 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:#

  1. Navigate to the folder containing your test file.

  2. 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_*.py or *_test.py

  • Test 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:#

  1. Run pytest with the --pdb flag:

    pytest --pdb
    
  2. When 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:#

  1. Write a Test: Begin by writing a test for the new functionality you want to implement.

  2. Run the Test: Since the functionality doesn’t exist yet, the test will fail.

  3. Write Code: Write just enough code to make the test pass.

  4. Run the Tests Again: Ensure all tests pass. If any tests fail, fix the code until they pass.

  5. Refactor: Clean up the code while ensuring that the tests continue to pass.

Example of TDD for Light Mixing:#

  1. Write the Test:

    def test_mix_colors():
        assert mix_colors("red", "blue") == "purple"
    
  2. Run the Test: It will fail because the mix_colors() function doesn’t exist yet.

  3. Write the Function:

    def mix_colors(color1, color2):
        if color1 == "red" and color2 == "blue":
            return "purple"
        return "unknown"
    
  4. Run the Test Again: The test should now pass.

  5. Refactor the Code: Improve the mix_colors() function while ensuring the test still passes.

Video Tutorial: Test-Driven Development with pytest

Additional Resources#

πŸš€ Quiz#

πŸ“„ Assignment#