Skip to main content
This guide outlines the testing strategy, standards, and best practices for the Definable backend codebase. It serves as a blueprint for developers to ensure consistent quality and reliability through automated testing.

Overview

A robust testing approach is crucial for maintaining code quality and ensuring that our services function correctly after changes. Our testing strategy comprises multiple levels:

Test Pyramid

The test pyramid represents the ideal distribution of different test types: This visualization demonstrates that:
  • Unit tests should form the majority of your test suite (fastest to run, easiest to write)
  • Integration tests should be fewer in number but cover critical component interactions
  • End-to-End tests should be the fewest but test complete user workflows

Test Directory Structure

Organize tests with a clear structure that mirrors the source code:

Setting Up the Testing Environment

Prerequisites

The project uses pytest for running tests. Dependencies are managed through Poetry and include:
  • pytest
  • pytest-asyncio (for testing async code)
  • pytest-cov (for coverage reporting)
These are already included in pyproject.toml.

Test Configuration

Create a conftest.py file at the root of the tests directory to define shared fixtures:

Test Fixture Dependencies

Unit Tests

Unit tests focus on testing individual components in isolation.

Writing Unit Tests for Models

Unit Test Flow

Writing Unit Tests for Services

Integration Tests

Integration tests verify that different components work together correctly.

Testing Service Integration

Integration Test Service Interaction

Testing Database Operations

CRUD Operations Sequence

End-to-End (E2E) Tests

E2E tests verify that the entire system works as expected.

Testing API Endpoints

E2E Test Flow

Mocking

Proper use of mocks is essential for isolation and controlled testing.

Mocking External Services

Mocking Concept Visualization

Test Fixtures

Create reusable fixtures to reduce code duplication and simplify tests.

Model Fixtures

Test Coverage

Aim for high test coverage of the codebase.

Running Coverage Reports

Coverage Targets

  • Unit Tests: Aim for 80%+ coverage of all functions and methods
  • Integration Tests: Cover all major service interactions
  • E2E Tests: Cover all API endpoints and primary user flows

Continuous Integration

Tests will be integrated into the CI/CD pipeline to ensure code quality.

CI/CD Workflow

GitHub Actions Configuration

Update the .github/workflows/pre-commit.yml file to include testing:

Testing Strategies by Component

Models

  • Test model instantiation
  • Test relationships between models
  • Test constraints and defaults

Services

  • Test business logic
  • Test error handling
  • Test edge cases
  • Mock external dependencies

API Endpoints

  • Test request validation
  • Test authentication/authorization
  • Test response format
  • Test error responses

Component Testing Focus

Best Practices

General Guidelines

  1. Test pyramid: Write more unit tests than integration tests, and more integration tests than E2E tests
  2. Test isolation: Each test should be independent and not rely on the state from other tests
  3. Descriptive naming: Use clear, descriptive names for test functions
  4. Arrange-Act-Assert: Structure tests with clear sections for setup, execution, and verification
  5. Don’t test implementation details: Focus on behavior, not implementation

Test Organization Pattern

Asynchronous Testing

Since our application uses async/await, follow these practices:
  1. Use pytest.mark.asyncio decorator for async test functions
  2. Use async fixtures where needed
  3. Handle event loops properly

Testing Database Operations

  1. Use transactions to isolate tests
  2. Roll back after each test
  3. Use a separate test database

Common Pitfalls

  1. Slow tests: Keep tests fast, especially unit tests
  2. Flaky tests: Avoid tests that sometimes pass and sometimes fail
  3. Over-mocking: Don’t mock everything; test real interactions where practical
  4. Hardcoded test data: Use fixtures and factories for test data
  5. Missing edge cases: Test error paths, not just happy paths

Testing Pitfalls and Solutions

Conclusion

A comprehensive testing strategy is essential for maintaining a healthy codebase. By following this guide, developers can write effective tests that ensure the reliability and quality of the Definable backend. Remember that tests are an investment in the future of the codebase, making it safer to refactor and extend functionality while maintaining confidence in the system’s behavior.

Additional Resources