Django Acceptance Testing with Robot Framework

Blog / Django · June 11, 2016 · Updated June 10, 2026 · 9 min read
Django Acceptance Testing with Robot Framework

Robot Framework is an open-source, keyword-driven test automation framework that you point at a running Django app to verify whole user journeys in plain, readable language. It sits on top of Python and lets QA and developers describe acceptance (end-to-end) tests as tables of keywords like New Page, Fill Text, and Click instead of imperative code — so business stakeholders can read the suite, and you can drive a real browser against http://127.0.0.1:8000.

The biggest change since older Robot Framework tutorials: for browser-based Django testing you should reach for the Browser Library (built on Microsoft Playwright) first. It is faster, auto-waits for elements, and ships modern Chromium/Firefox/WebKit support — and it is the library the Robot Framework community now recommends over the older SeleniumLibrary for new projects.

Key takeaways

  • Robot Framework = acceptance / E2E layer. Use it for human-readable, keyword-driven tests of full journeys; keep Django's TestCase for fast unit and view tests.
  • Prefer the Browser Library (Playwright) over SeleniumLibrary for new Django suites — auto-wait, faster runs, no separate WebDriver to manage.
  • Two install commands get you running: pip install robotframework robotframework-browser then rfbrowser init (downloads the bundled browsers).
  • Write .robot files with *** Settings ***, *** Variables ***, and *** Test Cases *** sections; run a whole folder with robot tests/.
  • Read the results in report.html (pass/fail summary), log.html (step detail), and output.xml (machine-readable source of truth).
  • Run it in CI (GitHub Actions or GitLab CI) against a Django instance you spin up in the job, and upload the reports as artifacts.

What is Robot Framework, and where does it fit in Django testing?

Robot Framework is a generic, keyword-driven automation framework implemented in Python. Tests are written as tabular data: each line is a keyword plus its arguments. Keywords come from imported libraries (such as Browser, SeleniumLibrary, or RequestsLibrary) or from higher-level user keywords you compose yourself — so a non-programmer can read User can sign up and log in and understand exactly what is verified.

In a typical Django test strategy you layer your tests like a pyramid:

Layer Tool What it checks Speed
Unit / view Django TestCase, pytest-django Models, forms, views in isolation (no real browser) Fast (ms)
Integration Django test client, RequestsLibrary Several components together, API contracts Medium
Acceptance / E2E Robot Framework + Browser Library Full user journeys in a real browser against a running app Slower (seconds)

Robot Framework owns the top of that pyramid. It does not replace manage.py test — it complements it by proving that the deployed, running application behaves correctly for an actual user.

Browser Library (Playwright) vs SeleniumLibrary: which should you use?

For new Django acceptance suites, start with the Browser Library. It wraps Playwright, so it auto-waits for elements to be actionable (far fewer flaky Sleep hacks), runs faster, and bundles its own browsers via rfbrowser init — no separate chromedriver/geckodriver to install and version-match. SeleniumLibrary is still perfectly valid, well documented, and the right choice if you must support legacy browsers or already have a large Selenium-based suite.

Aspect Browser Library (Playwright) SeleniumLibrary (WebDriver)
Underlying engine Microsoft Playwright Selenium WebDriver
Waiting model Auto-wait built in (actionability checks) Manual / explicit waits often needed
Speed Faster (persistent browser contexts) Slower (new WebDriver session overhead)
Browser setup rfbrowser init bundles Chromium/Firefox/WebKit Install + version-match chromedriver/geckodriver
Browser support Chromium, Firefox, WebKit Chrome, Firefox, Edge, Safari, legacy
Flakiness Low (network/DOM auto-wait) Higher without careful waits
Best for New, modern Django E2E suites Existing Selenium suites, legacy browser needs

The rest of this guide leads with the Browser Library and shows the SeleniumLibrary equivalent where it helps.

How do you install Robot Framework for a Django project?

Install Robot Framework and the Browser Library into your project's virtual environment, then run the one-time rfbrowser init step, which downloads the Node dependencies and Playwright browser binaries the library drives:

# Robot Framework + the modern, Playwright-based Browser Library
pip install robotframework robotframework-browser

# one-time: download the bundled Playwright browsers + node deps
rfbrowser init

# (optional) the classic Selenium-based alternative
pip install robotframework-seleniumlibrary

Pin these in your requirements-dev.txt so CI installs the same versions. rfbrowser init needs Node.js available on the machine; most CI Python images already include it, and you only run it once per environment.

How do you write your first .robot acceptance test?

Create a tests/ folder and add an acceptance.robot suite. A suite has named sections: *** Settings *** (libraries and setup/teardown), *** Variables *** (reusable values like the base URL), and *** Test Cases *** (the scenarios). The example below uses the Browser Library to drive a real browser against the Django dev server, exercise signup and login, and assert on what the user sees:

*** Settings ***
Library             Browser
Suite Setup         New Browser    chromium    headless=True
Suite Teardown      Close Browser
Test Setup          New Context    viewport={'width': 1280, 'height': 720}
Test Teardown       Close Context

*** Variables ***
${BASE_URL}         http://127.0.0.1:8000

*** Test Cases ***
User can sign up and log in
    New Page        ${BASE_URL}/accounts/signup/
    Fill Text       input[name="username"]     fred
    Fill Text       input[name="password1"]    P4ssw0rd!
    Fill Text       input[name="password2"]    P4ssw0rd!
    Click           button[type="submit"]
    Get Text        css=.messages              *=    Welcome, fred

User cannot log in with a bad password
    New Page        ${BASE_URL}/accounts/login/
    Fill Text       input[name="username"]     fred
    Fill Text       input[name="password"]     wrong-pass
    Click           button[type="submit"]
    Get Text        css=.errorlist             *=    correct username and password

Notice how close the test cases read to plain English — that readability is the whole point of acceptance testing. The *= operator asserts that the element text contains the expected string. Because the Browser Library auto-waits, you rarely need explicit Sleep calls; it waits for each selector to be actionable before acting.

To avoid repeating selectors across suites, extract reusable steps into a resource file (resources/auth.resource) and import it with Resource resources/auth.resource. User keywords compose existing keywords using the same tabular syntax:

*** Keywords ***
Log In As
    [Arguments]    ${username}    ${password}
    New Page       ${BASE_URL}/accounts/login/
    Fill Text      input[name="username"]    ${username}
    Fill Text      input[name="password"]    ${password}
    Click          button[type="submit"]

How do you run the suite and read the reports?

Point the robot command at a single file or a whole folder. Make sure the Django dev server is already running in another terminal (python manage.py runserver) so the tests have a live target:

# run a single suite
robot tests/acceptance.robot

# run every .robot suite under a folder, write artifacts to results/
robot --outputdir results tests/

# run only smoke-tagged tests
robot --include smoke tests/

Every run produces three artifacts. Open report.html first for the verdict, then drill into log.html when something fails:

File Purpose
report.html High-level pass/fail summary with statistics and tags
log.html Detailed, step-by-step execution log (best for debugging failures)
output.xml Machine-readable source of truth; can be merged or re-processed

output.xml is the canonical result. You can re-generate the HTML reports from it, or combine multiple runs, with rebot — for example rebot --output merged.xml results/*/output.xml. The command exits non-zero when any test fails, which is exactly what CI needs to fail the build.

What does the SeleniumLibrary version look like?

If you maintain an existing Selenium suite, the same login scenario reads like this. The main differences are Open Browser/Close Browser lifecycle keywords and that you may need explicit waits for slow pages:

*** Settings ***
Library    SeleniumLibrary

*** Variables ***
${BASE_URL}      http://127.0.0.1:8000
${BROWSER}       headlesschrome

*** Test Cases ***
User can log in
    Open Browser         ${BASE_URL}/accounts/login/    ${BROWSER}
    Input Text           name:username    fred
    Input Password       name:password    P4ssw0rd!
    Click Button         css:button[type="submit"]
    Wait Until Page Contains    Dashboard
    [Teardown]    Close Browser

How do you run Django acceptance tests in CI?

The pattern is the same in any CI: provision a database, install dependencies, run migrations, start Django in the background, wait for it to answer, then run robot and upload the reports. Here is a GitHub Actions workflow:

name: acceptance-tests
on: [push, pull_request]

jobs:
  robot:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: app
          POSTGRES_USER: app
          POSTGRES_PASSWORD: app
        ports: ['5432:5432']
        options: >-
          --health-cmd pg_isready --health-interval 10s
          --health-timeout 5s --health-retries 5
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install robotframework robotframework-browser
          rfbrowser init
      - name: Migrate
        run: python manage.py migrate
      - name: Start Django
        run: python manage.py runserver 127.0.0.1:8000 &
      - name: Wait for the server
        run: |
          for i in $(seq 1 30); do
            curl -sf http://127.0.0.1:8000/ && break || sleep 1
          done
      - name: Run acceptance tests
        run: robot --outputdir results tests/
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: robot-reports
          path: results/

The GitLab CI equivalent uses a postgres service and the same start-then-test flow. If you already run your unit tests in self-hosted GitLab with Docker, see Automate Django Tests in GitLab CI with Docker for the runner and image setup, then bolt the acceptance job on top:

acceptance:
  image: python:3.12
  services:
    - postgres:16
  variables:
    POSTGRES_DB: app
    POSTGRES_USER: app
    POSTGRES_PASSWORD: app
    DATABASE_URL: "postgres://app:app@postgres:5432/app"
  script:
    - pip install -r requirements.txt robotframework robotframework-browser
    - rfbrowser init
    - python manage.py migrate
    - python manage.py runserver 127.0.0.1:8000 &
    - sleep 5
    - robot --outputdir results tests/
  artifacts:
    when: always
    paths:
      - results/

Seed predictable test data before the run so assertions are deterministic — a fixture loaded with manage.py loaddata, or model factories. We prefer factories for acceptance setup because they stay in sync with your models; see factory_boy: a better alternative to fixtures.

How do acceptance tests fit with unit tests and coverage?

Acceptance tests answer "does the whole app work for a user?" while Django's TestCase answers "does this function or view work in isolation?". You want both: a thick base of fast unit tests and a thin, high-value layer of Robot Framework journeys for critical flows (signup, checkout, payments).

One caveat: Robot Framework runs the app as an external process, so a normal coverage run around your unit tests will not count lines exercised by acceptance tests. Track unit-test coverage separately with coverage.py — see Check test coverage in Django with coverage.py and Coveralls — and treat acceptance-test pass rate as its own quality signal rather than folding it into the coverage percentage.

This layered approach is how our team ships fast: small, fast unit tests on every commit and Robot Framework acceptance suites guarding the journeys that matter. If you would rather hand the whole quality pipeline to a partner, MicroPyramid has 12+ years and 50+ delivered projects building and testing Django applications — our software testing services cover acceptance, integration, and CI automation end to end.

Frequently Asked Questions

Is Robot Framework better than pytest or Django's TestCase?

They solve different problems, so it is not either/or. Django's TestCase and pytest-django are for fast unit and view tests that run in-process without a browser. Robot Framework is for human-readable acceptance/E2E tests that drive a real browser against a running app. Use unit tests for breadth and Robot Framework for the critical user journeys.

Should I use the Browser Library or SeleniumLibrary?

For new Django projects, start with the Browser Library (built on Playwright). It auto-waits for elements, runs faster, and bundles its own browsers via rfbrowser init, which removes the WebDriver version-matching pain. Choose SeleniumLibrary if you have an existing Selenium suite or must support legacy browsers it covers.

What does rfbrowser init do?

rfbrowser init is a one-time setup command from robotframework-browser. It downloads the Node.js dependencies and the Playwright browser binaries (Chromium, Firefox, WebKit) that the Browser Library drives. You run it once per environment after pip install robotframework-browser, including in CI. It needs Node.js on the machine.

How do I run acceptance tests against a running Django server?

Start the app first — python manage.py runserver 127.0.0.1:8000 — then run robot tests/ with your suite pointing ${BASE_URL} at that address. In CI, start Django in the background, poll the URL with curl until it answers, then run robot. Seed deterministic data with a fixture or factories before the run.

Can Robot Framework test Django REST APIs too?

Yes. Install robotframework-requests and use the RequestsLibrary to call endpoints, then assert on status codes and JSON bodies — no browser required. This makes Robot Framework a single place to keep both browser-based acceptance tests and API-level acceptance tests for a Django + DRF backend.

How do acceptance tests affect my code coverage numbers?

They usually don't show up in standard coverage.py reports, because Robot Framework runs Django as a separate process that your coverage run does not wrap. Measure unit-test coverage with coverage.py and treat the acceptance suite's pass rate as a separate quality gate, rather than expecting it to raise your coverage percentage.

Share this article