Skip to content

PyWorkout — Testing

PyWorkout's test suite lives in tests/ and runs on pytest. It covers the CLI in main.py; gui.py is an unfinished Tkinter frontend that is not wired into the application, so its tests skip in most environments.

For local setup, linting, and building, see Development.

Test Structure

tests/
├── __init__.py          # Package initialization
├── test_main.py         # Tests for main.py (CLI functionality)
└── test_gui.py          # Tests for gui.py (GUI components)

Test Coverage

The test suite includes the following test categories:

Main Module Tests (test_main.py)

  1. TestWorkoutData - Tests workout data structures
  2. Validates workout groups are defined correctly

  3. TestMuscleGroupSelection - Tests muscle group selection

  4. Selection by number (1-7)
  5. Selection by name (abs, quads, glutes, etc.)
  6. Invalid selection handling
  7. Quit during selection

  8. TestWorkoutCommands - Tests CLI commands

  9. list - List exercises
  10. start - Start workout
  11. help - Display help
  12. license - Display license
  13. quit - Exit program
  14. Invalid command handling

  15. TestWorkoutFlow - Tests workout flow

  16. Start → Next → End flow
  17. Skip functionality
  18. Stats command

  19. TestVideoFunctionality - Tests video playback

  20. Video command on different platforms

  21. TestWelcomeScreen - Tests welcome screen

  22. Welcome message display
  23. Day recommendation

  24. TestIntegration - Integration tests

  25. Complete workout scenarios
  26. Multiple muscle groups

GUI Module Tests (test_gui.py)

  1. TestGUIImports - Tests GUI imports
  2. TestPercentageFunction - Tests percentage calculations
  3. TestGUIComponents - Tests GUI components

Running Tests

Prerequisites

Install test dependencies:

pip install -r requirements.txt

This installs:

  • pytest
  • pytest-cov (coverage reporting)
  • pytest-mock (mocking support)

Running All Tests

Run all tests with coverage:

pytest tests/ -v

Running Specific Test Files

Run main module tests:

pytest tests/test_main.py -v

Run GUI tests:

pytest tests/test_gui.py -v

Running Specific Test Classes

pytest tests/test_main.py::TestMuscleGroupSelection -v

Running Specific Tests

pytest tests/test_main.py::TestMuscleGroupSelection::test_abs_selection_by_number -v

Coverage Reports

Generate Coverage Report

pytest tests/ --cov=. --cov-report=term-missing

Generate HTML Coverage Report

pytest tests/ --cov=. --cov-report=html

Then open htmlcov/index.html in your browser.

Generate XML Coverage Report

pytest tests/ --cov=. --cov-report=xml

Test Configuration

Test configuration is stored in setup.cfg:

[tool:pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts =
    -v
    --strict-markers
    --tb=short
    --cov=.
    --cov-report=term-missing
    --cov-report=html
    --cov-report=xml
    --cov-branch

Continuous Integration

Tests are automatically run via GitHub Actions on:

  • Push to main and develop branches
  • Pull requests to main and develop branches

The workflow tests against multiple Python versions:

  • Python 3.9
  • Python 3.10
  • Python 3.11
  • Python 3.12

See .github/workflows/tests.yml for the full workflow configuration.

Writing New Tests

When adding new tests, follow these guidelines:

  1. Naming Convention
  2. Test files: test_*.py
  3. Test classes: Test*
  4. Test functions: test_*

  5. Test Organization

  6. Group related tests in classes
  7. Use descriptive test names
  8. Add docstrings explaining what is being tested

  9. Mocking

  10. Use @patch for mocking input/output
  11. Mock external dependencies (filesystem, network, etc.)

  12. Example Test

@patch('builtins.print')
@patch('builtins.input')
def test_abs_selection(mock_input, mock_print):
    """Test selecting abs muscle group."""
    mock_input.side_effect = ['abs', 'quit']

    with pytest.raises(SystemExit):
        main.workout()

    printed_output = [str(call) for call in mock_print.call_args_list]
    assert any('Ab muscle group selected' in str(call) for call in printed_output)

Troubleshooting

GUI Tests Skipped

GUI tests may be skipped in headless environments (CI/CD). This is expected behavior as tkinter requires a display.

Import Errors

If you encounter import errors, ensure you're running tests from the project root:

cd /path/to/PyWorkout
pytest tests/

Coverage Not Showing

Ensure pytest-cov is installed:

pip install pytest-cov

Test Results

Current test coverage: ~54% overall

  • Main module: ~51% coverage
  • Test suite: 25 tests passing
  • GUI tests: 5 tests (may skip in headless environments)

Contributing

When contributing:

  1. Write tests for new features
  2. Ensure all tests pass before submitting PR
  3. Aim for >80% code coverage for new code
  4. Follow existing test patterns and conventions