name: write-e2e-tests description: Use when writing or modifying end-to-end tests in the Alkaa project — triggers on tasks like "add an E2E test", "write a flow test", "test this feature end-to-end", "add a test to shared module", or "cover this user journey with a test".
Write E2E Tests
Overview
E2E tests exercise full application flows through a real Koin DI graph and a live AlkaaMultiplatformApp Compose tree. They live in shared/src/commonTest/kotlin/com/escodro/alkaa/ — one file per feature domain (e.g., TaskFlowTest, CategoryFlowTest).
Structure
- Class setup — Extends
AlkaaTest(), KoinTest;@BeforeTestcallsbeforeTest()first, then cleans DAOs → seereferences/SETUP.md - Entry point —
= uiTest { }for all tests;= flakyUiTest { }only for known timing-sensitive tests → seereferences/SETUP.md - Data seeding — Insert directly via DAOs in
@BeforeTestusingrunTest→ seereferences/SETUP.md - Navigation helpers — Private
ComposeUiTestextension functions using content descriptions → seereferences/SETUP.md - Assertions — Existence, wait helpers, selection state, semantic matchers → see
references/SETUP.md - Fake dependencies —
declare<T> { FakeImpl() }to override external dependencies → seereferences/SETUP.md
Naming
Descriptive, underscore-separated. Two styles:
- State-driven:
when_no_query_then_all_tasks_are_visible() - Action-driven:
add_and_open_task(),complete_task_from_list()
No camelCase, no backtick names.
Rules
| Rule | Details |
|---|---|
beforeTest() first |
Call beforeTest() before any DAO injection or by inject() use |
cleanTable() in @BeforeTest |
Call on every DAO used to avoid state leakage between tests |
| DAO-based seeding | Insert test data directly via DAOs — not via use cases |
| Content descriptions for navigation | Navigate via content descriptions, not raw text |
| Resource strings | getString(Res.string.xyz) inside uiTest or runComposeUiTest — never hardcode |
| Fakes over mocks | Use declare<T> { FakeImpl() } for external dependencies |
@AfterTest always |
Always call afterTest() to stop Koin after each test |
Common Mistakes
| Mistake | Fix |
|---|---|
Calling by inject() before beforeTest() |
Always call beforeTest() first |
Skipping cleanTable() |
Call on every DAO to avoid state leakage |
Using flakyUiTest by default |
Reserve for tests with known timing sensitivity |
| Hardcoding UI strings | Use getString(...) inside uiTest or runComposeUiTest |
| Testing multiple flows in one test | One scenario per test |
Missing @AfterTest / afterTest() |
Always stop Koin after each test |
| Navigation via raw text | Navigate via content descriptions |
| Using mocks | Use fakes via declare<T> { FakeImpl() } |