Testing

Powered by Pytest. Used for surgically poking the application to verify desired responses.

Properties: Pytest files

  • ./pytest.ini

  • ./apps/conftest.py

Properties: VSCode shortcuts

  • Ctrl + R: Refresh tests

  • Ctrl + S: Run all

  • Ctrl + E: Run at cursor

  • Ctrl + Tab: Debug last

  • `Ctrl + ``: Debug at cursor

Properties: Pytest plugins

  • pytest-django: the man.

  • pytest-playwright

  • pytest-base-url

  • pytest-cov

  • pytest-env

  • pytest-factoryboy

  • pytest-redis

  • pytest-xdist

Testing utils.py

Provide input to utility functions, verify if output corresponds with expectations.

Workflow: Writing a test for a utility function

  1. Create ./apps/my_app/tests/test_utils.py, load pytest-django plugin:

    import pytest
    pytestmark = [pytest.mark.django_db]
    
  2. Import the utility function:

    from apps.my_app.utils import my_utility_function
    
  3. Define the test function with a clear name. Load setup fixtures if needed:

    def test_my_utility_function_returns_expected_value():
    
  4. Define input data and call the function:

        input_data = "example"
        result = my_utility_function(input_data)
    
  5. Assert that the output matches expectations:

        assert result == "expected_output"
    

Fixtures

Function to get reusable context for tests.

Workflow: Create customizable fixture to setup test context

  1. Create base fixture

    @pytest.fixture
    def create_author(request):
        Author.objects.create(name=f'Author 1')
    
  2. Identify the need of customization.

  3. Customize to meet the need:

    @pytest.fixture
    def create_author(request):
        params = getattr(request, 'param', {})
        if 'author_count' in params:
            for i in range(params['author_count']):
                Author.objects.create(name=f'Author {i}')
        return client
        
    @pytest.mark.parametrize('create_author', [{'author_count': 3}], indirect=True)
    def test_create_many_authors(create_author):
        ...
    

Definition: client fixture with logged in TestClient

Always logged in client

from django.contrib.auth.models import User

@pytest.fixture
def client(client):
    username='testuser'
    password='testpassword123'
    user = User.objects.create_user(
        username=username,
        password=password
    )
    client.login(
        username=username,
        password=password
    )
    return client