Files
werkator/docs/prs/2026-10-01-PR#91-separate-unit-and-integration-tests.md
miandmhoennig a4c0e52265
werkator build succeeded after 02:20
werkator/deploy build succeeded after 00:07
Separate unit tests from integration tests by name (v1.27.2) (#91)
Groundwork for mutation testing with PIT and werkcrappit.

- Every spec ends in `*UnitTest` (no Spring, no real git/process), `*RestTest` (`@WebMvcTest` slice) or `*IntegrationTest` (full context, real git, processes, WireMock, containers): 55 / 9 / 14; 77 files renamed, file and class.
- Gradle tasks `unitTest` (UnitTest + RestTest, 16 s test time) and `integrationTest` (40.5 s); `test` and CI unchanged.
- `TestNamingConventionUnitTest` fails for a spec without a suffix.
- AGENTS.md and the writing-tests skill describe the convention.

Found on the way: PIT's timeouts on Werkator came from its 4 s limit, not from the integration tests (323 → 2 timeouts at 20 s); that goes into the next PR together with PIT and werkcrappit.

CI on werkator.javagil.de: green (141 s).

PR-doc: `docs/prs/2026-10-01-PR#91-separate-unit-and-integration-tests.md`

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: mhoennig <michael@hoennig.de>
Reviewed-on: #91
2026-10-02 07:16:30 +02:00

2.9 KiB

PR#91: Separate Unit Tests From Integration Tests

This document describes the change of this pull request at the time it was made. Later pull requests may change the behaviour without updating it.

The Problem

Werkator's 78 specs had no way to tell a cheap test from an expensive one. Mutation testing is to come next (PIT with werkcrappit), and PIT runs the selected tests once per mutant in its own process. A spec that starts the full Spring context, creates real git repositories or runs processes costs seconds every time. Locally there is also no way to run just the fast half while working.

A probe run of PIT on the package metrics on 2026-10-01 showed how much the selection matters, and that it is not all: with only the seven unit specs of the package, 323 of 787 mutants timed out at PIT's default limit of 4 s, two at 20 s. The limit is the next PR's business; this one makes the selection possible.

Non-Goals

  • PIT itself, werkcrappit and the timeout setting: the next PR.
  • Changing what a test does: only names, tasks and documentation change.
  • Separate source sets: the suffix is enough for Gradle's filter and for PIT's targetTests.

The Scenarios

Scenario#91.01: every spec says what it needs

  • Given the specs under src/test/kotlin
  • Then each name ends in UnitTest, RestTest or IntegrationTest
  • And a spec without one of them fails the build, since it would run in neither task
Verified by

Scenario#91.02: the cheap and the expensive half run on their own

  • When ./gradlew unitTest runs
  • Then the 55 *UnitTest and 9 *RestTest specs run, plus the naming test, 16 s of test time
  • When ./gradlew integrationTest runs
  • Then the 14 *IntegrationTest specs run, 40.5 s of test time
  • And ./gradlew test and CI still run all of them
Verified by
  • local runs of both tasks on 2026-10-01; the CI build of this branch runs test

The Solution

The suffixes follow hsadmin-ng:

  • *UnitTest — no Spring context, no real git, sh or other process; temporary files and MockK are fine.
  • *RestTest — a @WebMvcTest slice; it counts as cheap, so that mutation testing covers the controllers.
  • *IntegrationTest — the full context, real git, processes, WireMock, Testcontainers or real waiting.

The 77 specs without a suffix were renamed, file and class; BuildExecutorArtifactIntegrationTest already had one. The classification was derived from what each spec uses and checked by hand; two first guesses were wrong (InitCommandTest and NginxProxyManagerTest only mock processes and waiting). build.gradle.kts registers unitTest and integrationTest with Gradle's test filter. AGENTS.md and the writing-tests skill describe the convention; maintained references to renamed tests were updated, historic PR-docs were not.