Guidelines on writing tests for the backend
The verification weight of this suite is carried by the API-level tests that run through the real stack (TestClient → router → use case → repository → in-memory SQLite). Mutation testing confirmed this empirically: logic bugs that the unit tier misses are caught at the API tier. Write tests accordingly.
New behavior means a new or extended API test in backend/tests/test_*.py, exercised
through the endpoint, not the internals.
Write a unit test (tests/unit/) only for pure domain logic with real branching: value
objects, parsers, domain services (e.g. PositionIndex, HighlightStyleResolver).
Litmus test: can you enumerate meaningful input classes without mocking anything? If the
setup needs AsyncMock, the behavior belongs in an API test instead.
Do NOT write mock-based tests for thin orchestration (application-layer use cases). A test asserting "the repository was called" mirrors the implementation: it breaks on refactors and survives logic bugs — the inversion of what a test is for.
# ✗ WRONG — tautological; verifies the mock you just configured
use_case = CreateNoteUseCase(note_repository=mock_repo)
await use_case.execute(command)
mock_repo.save.assert_called_once()
# ✓ CORRECT — the API test covers the same wiring with real behavior
response = client.post("/api/notes", json={"title": "..."})
assert response.status_code == 201
assert response.json()["title"] == "..."
user_id filter is this app's worst realistic bug class, and only
a deliberate test catches it.After writing a test, temporarily break the code under test (invert the condition, short-circuit the function), confirm the test fails, then revert the breakage. A test that cannot fail is worse than no test. Corollary: include at least one input where the expected output differs from the input — a test whose inputs are all fixed points of the function under test verifies nothing.
Do not test exception message wiring, framework behavior, or anything fully shadowed by a stronger API test. When you find a weak test (assertion-free, tautological, fully shadowed), delete it. Fewer strong tests beat many weak ones.
tests/conftest.py
fixtures before adding new ones.cd backend && uv run pytest.make mutation-test (mutmut over src/domain/). Surviving
mutants are concrete test gaps — write tests that kill them. Mutants reported as
timeout are inconclusive rather than gaps: mutmut forks a warm process and our
DB-backed tests hang after the fork, so read the audit for survivors.npx skills add Crossbill-Highlights/编写测试下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer