githubEdit

Integration Testing

This guide explains how to write integration tests for the Gyrinx application using Django's test client and pytest.

Overview

Integration tests verify complete user workflows through the UI, ensuring all components work together correctly. They use Django's test client to simulate HTTP requests and responses, testing the full stack from URLs to views to models.

Test Structure

Integration tests follow these conventions:

  1. File naming: Use test_integration_*.py for integration test files

  2. Function naming: Use descriptive names that explain the workflow being tested

  3. Decorators: Always use @pytest.mark.django_db for tests that access the database

Basic Pattern

@pytest.mark.django_db
def test_user_workflow(client, user, other_fixtures):
    """Test description of what workflow is being tested."""
    # 1. Login user if authentication is required
    client.force_login(user)

    # 2. Make GET request to view page
    response = client.get(reverse("app:view-name", args=[obj.id]))
    assert response.status_code == 200
    assert "Expected content" in response.content.decode()

    # 3. Make POST request to submit form
    response = client.post(
        reverse("app:action-name", args=[obj.id]),
        {
            "field1": "value1",
            "field2": "value2",
        }
    )
    assert response.status_code == 302  # Redirect after success

    # 4. Verify database changes
    obj.refresh_from_db()
    assert obj.field1 == "value1"

    # 5. Verify UI reflects changes
    response = client.get(reverse("app:view-name", args=[obj.id]))
    assert "value1" in response.content.decode()

Available Fixtures

The project provides several fixtures in conftest.py:

  • client: Django test client for making HTTP requests

  • user: A test user instance

  • make_user: Factory for creating users

  • content_house: A ContentHouse instance

  • make_content_house: Factory for creating houses

  • content_fighter: A ContentFighter instance

  • make_content_fighter: Factory for creating fighters

  • make_list: Factory for creating lists

  • make_list_fighter: Factory for creating list fighters

  • make_equipment: Factory for creating equipment

  • make_weapon_profile: Factory for creating weapon profiles

  • make_weapon_accessory: Factory for creating weapon accessories

Common Test Scenarios

Testing Authentication

Testing Form Submissions

Testing Permissions

Testing Search and Filtering

Best Practices

  1. Test complete workflows: Integration tests should cover entire user journeys, not just individual views

  2. Use descriptive assertions: Check for specific content in responses to ensure the right template and data are rendered

  3. Test error cases: Verify that invalid inputs are handled gracefully

  4. Clean test data: Tests should create their own data and not depend on existing database state

  5. Test permissions: Always verify that unauthorized users cannot access protected resources

  6. Use factories: Leverage the provided fixture factories to create test data consistently

Running Integration Tests

Run all integration tests:

Run a specific test:

Run with verbose output:

Debugging Tips

  1. Print response content: When a test fails, print response.content.decode() to see the actual HTML

  2. Check redirects: Use response.url to see where a redirect is going

  3. Examine form errors: Access response.context['form'].errors to see validation errors

  4. Database state: Use Model.objects.all() to verify database changes

  5. Use pdb: Add import pdb; pdb.set_trace() to debug interactively

Example: Complete Integration Test

Here's a complete example testing the fighter equipment workflow:

This example demonstrates:

  • Setting up test data with fixtures

  • Making authenticated requests

  • Testing multiple related views

  • Verifying database changes

  • Checking UI updates reflect data changes

Last updated