Skip to content

Commit 1b7299e

Browse files
committed
Use behave to document acceptance tests for clinical review
1 parent 0b2cf7a commit 1b7299e

File tree

11 files changed

+285
-20
lines changed

11 files changed

+285
-20
lines changed

behave.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[behave]
2+
paths = tests/features
3+
format = pretty
4+
tags = ~@skip
5+

lung_cancer_screening/settings_test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@
1313
"whitenoise.middleware.WhiteNoiseMiddleware",
1414
)
1515

16+
INSTALLED_APPS += ["behave_django"]
17+
1618
logging.disable(logging.CRITICAL)

poetry.lock

Lines changed: 154 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description = ""
55
authors = [{name = "Your Name",email = "[email protected]"}]
66
license = { text = "MIT" }
77
readme = "README.md"
8-
requires-python = ">=3.13"
8+
requires-python = ">=3.13, <4.0"
99
dependencies = [
1010
"azure-identity (>=1.23.0,<2.0.0)",
1111
"django (>=5.2.4,<6.0.0)",
@@ -22,7 +22,13 @@ package-mode = false
2222
[tool.poetry.group.dev.dependencies]
2323
ruff = "^0.14.5"
2424
playwright = "^1.56.0"
25+
behave-django = "^1.8.0"
2526

2627
[build-system]
2728
requires = ["poetry-core>=2.0.0,<3.0.0"]
2829
build-backend = "poetry.core.masonry.api"
30+
31+
[tool.behave]
32+
junit = true
33+
junit_directory = "tests"
34+
paths = ["tests"]

scripts/tests/ui.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docker compose run --rm web poetry run python manage.py behave --settings=lung_cancer_screening.settings_test
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Feature: Participants with submitted responses
2+
Scenario: Cannot change responses once submitted
3+
Given a participant abc123 exists
4+
And the participant abc123 has submitted their responses
5+
And the participant is on the "/start" path
6+
When the participant abc123 submits their participant id
7+
Then the participant should be on the "/start" path
8+
And the participant should see en error summary "Responses have already been submitted for this participant"

tests/features/environment.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"""
2+
Behave environment setup for Django tests.
3+
behave-django handles test database setup automatically.
4+
We just need to add live server and Playwright setup.
5+
"""
6+
import os
7+
from django.contrib.staticfiles.testing import StaticLiveServerTestCase # noqa: E402
8+
from playwright.sync_api import sync_playwright # noqa: E402
9+
10+
# behave-django automatically handles:
11+
# - Django setup
12+
# - Test database creation
13+
# - Database transactions per scenario
14+
15+
16+
class LiveServer(StaticLiveServerTestCase):
17+
"""Live server for Behave tests - extends Django's StaticLiveServerTestCase."""
18+
19+
@classmethod
20+
def setUpClass(cls):
21+
"""Set up live server - called once for all scenarios."""
22+
super().setUpClass()
23+
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
24+
25+
26+
def before_all(context):
27+
"""Set up before all tests run."""
28+
# Set up live server (behave-django handles test database)
29+
LiveServer.setUpClass()
30+
context.live_server_url = LiveServer.live_server_url
31+
context.live_server_class = LiveServer
32+
33+
# Set up Playwright browser
34+
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
35+
context.playwright = sync_playwright().start()
36+
context.browser = context.playwright.chromium.launch(headless=True)
37+
38+
39+
def after_all(context):
40+
"""Clean up after all tests run."""
41+
# Clean up Playwright
42+
if hasattr(context, 'browser'):
43+
context.browser.close()
44+
if hasattr(context, 'playwright'):
45+
context.playwright.stop()
46+
47+
# Tear down live server (behave-django handles test database teardown)
48+
if hasattr(context, 'live_server_class'):
49+
LiveServer.tearDownClass()
50+
51+
52+
def before_scenario(_context, _scenario):
53+
"""Set up before each scenario."""
54+
# behave-django automatically handles database transactions per scenario
55+
pass
56+
57+
58+
def after_scenario(context, _scenario):
59+
"""Clean up after each scenario."""
60+
# Close the page if it exists
61+
if hasattr(context, 'page'):
62+
context.page.close()
63+
del context.page
64+
65+
# behave-django automatically rolls back database transactions
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from behave import then
2+
from playwright.sync_api import expect
3+
4+
5+
@then(u'the participant should see en error summary "{error_summary}"')
6+
def then_the_participant_should_see_an_error_summary(context, error_summary):
7+
expect(context.page.locator('#maincontent')).to_contain_text(error_summary)

tests/features/steps/form_steps.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from behave import when
2+
3+
4+
@when('the participant {participant_id} submits their participant id')
5+
def when_the_participant_submits_their_participant_id(context, participant_id):
6+
context.page.fill('input[name="participant_id"]', participant_id)
7+
context.page.click('button[type="submit"]')
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from behave import given, then
2+
from playwright.sync_api import expect
3+
4+
5+
@given('the participant is on the "{path}" path')
6+
def given_the_participant_is_on_the_path(context, path):
7+
context.page = context.browser.new_page()
8+
context.page.goto(f"{context.live_server_url}{path}")
9+
10+
@then('the participant should be on the "{path}" path')
11+
def then_the_participant_should_be_on_the_path(context, path):
12+
expect(context.page).to_have_url(f"{context.live_server_url}{path}")

0 commit comments

Comments
 (0)