| name | write-unit-tests |
| description | Use when writing or modifying unit tests in the Alkaa project — triggers on tasks like "add a test", "write tests for X", "test this ViewModel", "cover this use case with tests", or "add unit test coverage". |
Write Unit Tests
Overview
Unit tests in Alkaa are multiplatform-first, fake-based, and structured around a single scenario per test. Tests live in commonTest and run fastest via ./gradlew desktopTest.
Structure
- Given / When / Then — Use comments to separate the three blocks in every test → see
references/TEST_EXAMPLES.md
- Backtick names — Start with
test if or check if, describe the specific condition and expected outcome
- One scenario per test — Split side effects, error paths, and edge cases into separate tests
- Fakes only — Never mocks for domain interfaces → see
references/FAKE_PATTERNS.md for all three fake patterns
- CoroutinesTestDispatcher — Delegate
by CoroutinesTestDispatcherImpl() for any coroutine-using test class → see references/TEST_EXAMPLES.md
- ViewModel test setup → see
references/TEST_EXAMPLES.md
- Use case / repository test setup → see
references/TEST_EXAMPLES.md
Naming
✅ `test if when load tasks fails the error state is returned`
✅ `test if task is updated as completed`
✅ `test if category without name is not added`
❌ testUpdateTask()
❌ test1()
Rules
| Rule | Details |
|---|
| Fakes over mocks | Only use mocks for Android/Framework types you cannot interface (e.g., Context) |
| CoroutinesTestDispatcher | Delegate by CoroutinesTestDispatcherImpl() for any coroutine-using test class |
| ViewModel at field level | Construct ViewModel at field level, not inside @BeforeTest |
runTest for suspend | Use = runTest { } for all suspend test bodies |
flow.first() | Collect current state with flow.first() |
require() for narrowing | Use require(state is X) before accessing state-specific properties |
@BeforeTest cleanup | Call clean() on every fake to reset state between tests |
Common Mistakes
| Mistake | Fix |
|---|
| Using mocks for domain interfaces | Create a fake implementing the interface |
Forgetting CoroutinesTestDispatcher delegation | Add by CoroutinesTestDispatcherImpl() |
| Testing multiple scenarios in one test | Split into separate @Test functions |
Skipping @BeforeTest cleanup | Add setup() calling clean() on every fake |
Constructing ViewModel in @BeforeTest | Construct at field level so it shares dispatcher context |
| Omitting Given/When/Then comments | Always add the three comment blocks |
Vague test names like testUpdate() | Rename to describe the scenario and expected outcome |
Related Skills
- ViewModel tests → use
write-viewmodel skill for ViewModel construction patterns
- UI behavior tests → use
write-ui-tests skill
- Full user flow tests → use
write-e2e-tests skill