Local Development & Testing Setup

Guide to setting up local development and testing with Docker and in-memory alternatives for Redis

Local Development & Testing Setup

This guide covers setting up local development and running tests.

Overview

ComponentProductionLocal DevTest
DatabasePostgreSQL (Neon)Docker PostgreSQLDocker PostgreSQL or TEST_DATABASE_URL
CacheRedisDocker RedisLocMemCache
ChannelsRedisDocker RedisInMemoryChannelLayer
CeleryRedis brokerEager mode (sync)Eager mode
EmailResendSMTP (Mailpit)LocMem backend

Local Development with Docker

Use compose.lean.yaml to run PostgreSQL, Redis, and Mailpit:

docker compose -f compose.lean.yaml up -d

This starts:

  • PostgreSQL on port 5432
  • Redis on port 6379
  • Mailpit on port 1025 (SMTP) and 8025 (web UI)

Redis Alternatives for Tests

Cache

Already configured to use LocMemCache for tests:

if TEST_ENV:
    CACHES = {
        "default": {
            "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
            "LOCATION": "",
        }
    }

Django Channels

Use the built-in InMemoryChannelLayer:

if TEST_ENV:
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "channels.layers.InMemoryChannelLayer",
        },
    }

Note: InMemoryChannelLayer is single-process only. For multi-process testing, use fakeredis.

Celery

Already configured for eager/synchronous execution in local/test:

if DJANGO_ENV == "local":
    CELERY_TASK_ALWAYS_EAGER = True
    CELERY_TASK_EAGER_PROPAGATES = True

Tasks execute synchronously without Redis.

fakeredis (Optional)

For code that directly uses Redis (not via Django cache/channels):

uv add --dev "fakeredis[lua]"
import fakeredis

# Create fake Redis connection
fake_redis = fakeredis.FakeRedis()

# Use like regular Redis
fake_redis.set("key", "value")
fake_redis.get("key")  # b'value'

# Pubsub supported
pubsub = fake_redis.pubsub()
pubsub.subscribe("channel")
fake_redis.publish("channel", "message")

fakeredis Limitations

  • No persistence - Data lost when process ends (same as Redis pubsub)
  • Single process - Doesn't work across multiple processes
  • Lua scripts - Requires fakeredis[lua] extra

Running Tests

# Set environment
export DJANGO_ENV=test

# Run all tests
cd backend && uv run pytest tests/ -v

# Run specific app tests
uv run pytest tests/dashboard/ -v

# With coverage
uv run pytest tests/ --cov=apps --cov-report=html

CI Configuration

Example GitHub Actions workflow with PostgreSQL service:

name: Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: pgvector/pgvector:pg16
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test_db
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      
      - name: Install uv
        uses: astral-sh/setup-uv@v4
      
      - name: Install dependencies
        run: |
          cd backend
          uv sync --frozen
      
      - name: Run tests
        env:
          DJANGO_ENV: test
          DJANGO_SECRET_KEY: test-secret-key
          DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db
        run: |
          cd backend
          uv run pytest tests/ -v

Troubleshooting

Channel layer issues in tests

For WebSocket tests, ensure you're using InMemoryChannelLayer:

@pytest.fixture
def channel_layer():
    from channels.layers import get_channel_layer
    return get_channel_layer()

Database connection issues

Ensure Docker is running and the database container is healthy:

docker compose -f compose.lean.yaml ps

Slow test startup

If tests are slow:

  1. Use --reuse-db pytest flag (already configured)
  2. Consider running tests in parallel with pytest-xdist

Last updated on

On this page