Testing
Test types
| Type | Location | Build tag | Runs by default |
|---|---|---|---|
| Unit | internal/<pkg>/*_test.go | none | yes |
| Integration | internal/<pkg>/*_integration_test.go | integration | only with -tags=integration |
| Golden | internal/<pkg>/testdata/<name>.golden | none | yes |
Unit tests
- One
_test.goper source file. - Table-driven where there are 3+ similar cases.
- Use
t.Run(name, ...)subtests so failures are addressable. - No external dependencies (no network, no filesystem outside
t.TempDir()).
Integration tests
- For anything exercising SQLite, the state machine, and the store together.
- Use an in-memory SQLite DB (
file::memory:?cache=shared) per test — fast, isolated. - Migrations run once per test via
storetest.New(t). - Build-tagged so casual
go test ./...stays fast; CI runs both untagged and-tags=integration.
Golden tests
- For report renderers and dashboard HTML.
- Update with
go test ./... -update. - Failures print a unified diff; do not auto-accept.
CLI tests
- Invoke
cmd/fairwayas a subprocess in tests undercmd/fairway/. - Use
t.TempDir()for repo + config + DB fixtures. - Assert on stdout, stderr, and exit code.
Coverage
No hard percentage gate. Instead:
- Every public function in
internal/storeandinternal/statehas at least one test. - Every CLI verb has at least one end-to-end test.
- Bug fixes include a regression test.
CI prints coverage; reviewers may push back on PRs that drop overall coverage by more than 2 percentage points.
Mocks
- Do not mock the database. Use real SQLite in-memory.
- Do not mock git. Use a real throwaway repo in
t.TempDir(). - The only thing we mock is the system clock, via a
nowFunc func() time.Timefield on the relevant struct.
Fixtures
testdata/next to the package under test.- Small (< 10KB) fixtures committed in-tree.
- Larger fixtures generated by
go test -gen-fixtures(when needed).
Flakes
A flaky test is a broken test. If you see a flake:
- Open an issue with the test name and failure output.
- Either fix it or quarantine with
t.Skip("flaky: see #N")and assign the issue.
Never t.Skip without an issue.