Skip to content

Testing and Coverage Guide

Jorge Miguel Silva edited this page Oct 15, 2024 · 1 revision

Testing and Coverage Guide

This guide provides comprehensive instructions on how to run tests, measure test coverage, and interpret the results for the project. Following these practices ensures code reliability, maintainability, and robustness.

Table of Contents

Overview

Testing is crucial for ensuring that your code works as intended and remains reliable as it evolves. This project utilizes Python's built-in unittest framework to create and run tests for various modules. Additionally, measuring test coverage helps identify untested parts of the codebase, guiding improvements in test suites.

Prerequisites

Before proceeding, ensure you have the following installed:

  • Python 3.6+
  • pip (Python package installer)

You may also need to install additional packages for coverage analysis.

Running Tests

The project uses Python's unittest framework for testing. Tests are organized within the tests/ directory, each targeting a specific module.

Using unittest

Python's unittest framework enables the creation and execution of test cases. Each test file corresponds to a module and contains multiple test classes and methods.

Running All Tests

To run all tests in the project:

  1. Navigate to the Project Root Directory

    Open your terminal and navigate to the root directory of the project.

    cd PhenoQC
  2. Execute the Test Suite

    Use the unittest discovery mechanism to automatically find and run all tests in the tests/ directory.

    python -m unittest discover -s tests

    Explanation:

    • -m unittest: Invokes the unittest module as a script.
    • discover: Tells unittest to discover tests.
    • -s tests: Specifies the starting directory (tests/) for discovery.

Running Specific Tests

To run tests from a specific test file or test case:

  1. Run a Specific Test File

    python -m unittest tests.test_validation
  2. Run a Specific Test Case within a Test File

    python -m unittest tests.test_validation.TestValidationModule
  3. Run a Specific Test Method within a Test Case

    python -m unittest tests.test_validation.TestValidationModule.test_validate_format

Running Tests with Verbose Output

For more detailed test output, use the -v flag:

python -m unittest discover -s tests -v

The verbose mode provides additional information about each test being run, which helps debug and understand test behavior.

Test Coverage

Test coverage measures the extent to which your codebase is exercised by your tests. Achieving high coverage helps ensure that your code is well-tested and reduces the likelihood of undetected bugs.

Installing Coverage

First, install the coverage package using pip:

pip install coverage

Running Coverage Analysis

  1. Navigate to the Project Root Directory

    cd PhenoQC
  2. Run Tests with Coverage

    Use coverage to execute your test suite and collect coverage data.

    coverage run -m unittest discover -s tests

    Explanation:

    • coverage run: Runs a command while measuring code coverage.
    • -m unittest discover -s tests: The command to execute—running all tests.

Generating Coverage Reports

After running tests with coverage, generate a coverage report to visualize the results.

  1. Generate a Terminal Report

    coverage report

    This command displays a summary of coverage statistics in the terminal.

  2. Generate an HTML Report

    coverage html

    This command creates an HTML report in the htmlcov/ directory, providing a detailed and navigable view of coverage data.

  3. Viewing the HTML Report

    Open the index.html file in the htmlcov/ directory using a web browser to explore the coverage results interactively.

    open htmlcov/index.html  # macOS
    xdg-open htmlcov/index.html  # Linux
    start htmlcov\index.html  # Windows

Interpreting Coverage Results

Coverage reports typically include the following metrics:

  • Total Coverage (%): The percentage of code covered by tests.
  • Per-Module Coverage: Detailed coverage statistics for each module.
  • Line Coverage: Indicates which specific lines of code were executed during tests.

Aim for high coverage, but prioritize meaningful tests over simply increasing coverage percentages. Ensure that critical and complex parts of the code are thoroughly tested.

Best Practices

  • Write Comprehensive Tests: Cover various scenarios, including edge cases and error conditions.
  • Maintain Test Independence: Ensure that tests do not depend on each other to prevent cascading failures.
  • Use Descriptive Test Names: Clearly describe what each test verifies to enhance readability and maintainability.
  • Regularly Update Tests: Keep tests in sync with code changes to maintain their effectiveness.
  • Automate Testing: Integrate tests into your development workflow, such as using pre-commit hooks or continuous integration (CI) pipelines.

Troubleshooting

Common Issues

  1. Tests Not Discovered

    • Issue: unittest cannot find test files or test cases.
    • Solution: Ensure that test files are named with the test_*.py pattern and that test classes inherit from unittest.TestCase.
  2. Coverage Not Capturing All Code

    • Issue: Some code lines are not covered in the coverage report.
    • Solution: Verify that all relevant code paths are executed by your tests. Update or add tests to cover missing areas.
  3. Import Errors During Testing

    • Issue: Tests fail due to import errors.
    • Solution: Check that the PYTHONPATH includes the project root or use relative imports appropriately.
  4. Failed Tests

    • Issue: Tests fail unexpectedly.
    • Solution: Investigate the failure messages, ensure that the code behaves as expected, and update tests or code as necessary.

Tips

  • Isolate Test Failures: Run individual tests to isolate and identify failing cases.

    python -m unittest tests.test_validation.TestValidationModule.test_detect_conflicts
  • Use Coverage Exclusions: Exclude specific files or lines from coverage analysis if they are not relevant (e.g., autogenerated code).

    Add a .coveragerc file to configure coverage settings.

    [run]
    omit =
        tests/*
        */__init__.py
  • Consistent Environment: Use virtual environments to maintain consistent dependencies across different environments.

    python -m venv venv
    source venv/bin/activate  # macOS/Linux
    venv\Scripts\activate  # Windows
    pip install -r requirements.txt