/blog/automating-django-tests-with-github-actions-cicd/ - zsh
user@portfolio ~ $

cat automating-django-tests-with-github-actions-cicd.md

Automating Django Tests with GitHub Actions (CI/CD)

Author: Aslany Rahim Published: November 24, 2025
Stop manually running tests. Set up a Continuous Integration pipeline that automatically tests your code every time you push to GitHub.

Deployment should not be scary. If you are afraid to push code on a Friday, it means you lack confidence in your testing pipeline.

Continuous Integration (CI) is the practice of automating the integration of code changes. In this tutorial, we will set up GitHub Actions to run our Django tests and lint our code automatically.

What is GitHub Actions?

GitHub Actions is a CI/CD platform built directly into GitHub. It allows you to define "Workflows" (YAML files) that trigger on specific events, like a push or pull_request.

Step 1: The Workflow File

Create a directory in your project root: .github/workflows/. Inside, create a file named django_test.yml.

name: Django CI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest

    # Service Containers: This spins up a temporary Postgres DB for testing
    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_DB: test_db
          POSTGRES_USER: user
          POSTGRES_PASSWORD: password
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - uses: actions/checkout@v3

    - name: Set up Python 3.10
      uses: actions/setup-python@v3
      with:
        python-version: "3.10"

    - name: Install Dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install flake8

    - name: Lint with Flake8
      run: |
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics

    - name: Run Tests
      env:
        # These match the service container above
        DB_ENGINE: django.db.backends.postgresql
        DB_NAME: test_db
        DB_USER: user
        DB_PASSWORD: password
        DB_HOST: localhost
        DB_PORT: 5432
      run: |
        python manage.py test

Step 2: Preparing Your Django Settings

Your settings.py needs to be able to read these environment variables. In a production or CI environment, you shouldn't hardcode database credentials.

import os

DATABASES = {
    'default': {
        'ENGINE': os.environ.get('DB_ENGINE', 'django.db.backends.sqlite3'),
        'NAME': os.environ.get('DB_NAME', 'db.sqlite3'),
        'USER': os.environ.get('DB_USER', ''),
        'PASSWORD': os.environ.get('DB_PASSWORD', ''),
        'HOST': os.environ.get('DB_HOST', ''),
        'PORT': os.environ.get('DB_PORT', ''),
    }
}

Step 3: Linting

Notice the Lint with Flake8 step in the YAML file? Linting checks your code for stylistic errors (like unused imports or indentation issues) before running the heavy tests. It's a "fail fast" mechanism.

Create a .flake8 configuration file in your root to ignore specific migrations files (which are auto-generated and often fail strict linting):

[flake8]
exclude = .git,__pycache__,migrations,env
max-line-length = 120

The Workflow

  1. You push code to GitHub.
  2. GitHub notices the .github/workflows file.
  3. It allocates a Linux server.
  4. It spins up a Docker container for Postgres.
  5. It installs Python and your dependencies.
  6. It runs python manage.py test.

If any test fails, you get a big red X on your Pull Request, preventing you from merging broken code.

Conclusion

Setting up CI takes about 15 minutes, but it saves hours of debugging "production bugs" later. It creates a safety net that allows you to refactor code with confidence.

25 views
0 comments

Comments (0)

Leave a Comment

No comments yet. Be the first to comment!

Related Posts

Keeping Your Stack Safe: Managing Dependencies in Python and Node

The Equifax breach was caused by a single unpatched library. Learn how to audit and update your dependencies using pip-audit …

November 30, 2025

Deploying Django to Production: Nginx and Gunicorn

The runserver command is not for production! Learn how to set up a robust production server using Gunicorn as the …

November 29, 2025

Dockerizing Django: From Development to Production

Stop the "it works on my machine" excuses. Learn how to containerize your Django and PostgreSQL application using Docker and …

November 19, 2025

user@portfolio ~ $ _