58 lines
2.8 KiB
Markdown
58 lines
2.8 KiB
Markdown
# PR#136: MockK Mocks Cleared after Every Spec
|
|
|
|
> **WARNING:** This document describes only the change applied in this PR.
|
|
> It may already be outdated once the next PR is merged.
|
|
> Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
|
|
|
|
## The Problem
|
|
|
|
The nightly mutation build of 2026-10-10 passed the Mutation-CRAP limit of 15 for the first time, but still went red.
|
|
One mutant in `Watcher.pruneWorktrees` ended in `MEMORY_ERROR`, so werkcrappit found its method `INCONCLUSIVE` and refused the gate.
|
|
|
|
The mutant itself is harmless: applied by hand, `WatcherUnitTest` kills it with two failing tests.
|
|
A local PIT run of `Watcher` alone, with the heap of a minion on mih09 (1484 MiB, a quarter of its RAM), ended the same mutant in `MEMORY_ERROR`.
|
|
With 3 GiB of heap, PIT killed it.
|
|
|
|
PIT runs all 190 mutants of `Watcher` in one minion, and for each mutant it runs the whole spec again.
|
|
The live heap of that minion grew by about 200 MB every 15 seconds, also right after a full GC.
|
|
After 70 seconds, a class histogram showed 123147 live `MockKStub` objects: every mock the minion had ever created.
|
|
|
|
MockK holds its stubs only weakly, but the answers and recorded calls of a stub point back to its mock.
|
|
So the weak registries never let a mock go.
|
|
A normal test run is short enough not to notice, but a minion runs every spec a few hundred times.
|
|
|
|
## Non-Goals
|
|
|
|
- Finding out why MockK still keeps the emptied stubs.
|
|
An emptied stub is small: with the fix, the heap of the minion grows four times slower, and no mutant ends in `MEMORY_ERROR`.
|
|
|
|
## The Scenarios
|
|
|
|
#### Scenario#136.01: A spec clears its mocks when it ends
|
|
|
|
- **Given** a spec with a mock that has an answer and a recorded call
|
|
- **When** the spec ends
|
|
- **Then** the mock has neither
|
|
- **So that** old mocks do not fill the heap of a PIT minion.
|
|
|
|
##### Verified by
|
|
|
|
- [ReleaseMocksAfterSpecUnitTest](../../src/test/kotlin/de/hoennig/werkator/framework/ReleaseMocksAfterSpecUnitTest.kt)
|
|
|
|
## The Solution
|
|
|
|
- The Kotest project configuration registers `ReleaseMocksAfterSpec`, which calls `clearAllMocks()` after every spec.
|
|
- After a spec, not after a test: a spec may stub its mocks once for all its tests.
|
|
No spec stubs a mock in a bean definition, so a Spring context cached for the next spec loses nothing it needs.
|
|
|
|
The same local PIT run of `Watcher`, with 1484 MiB and the fix:
|
|
|
|
| Run | Live heap growth (after full GC) | Mutants in `MEMORY_ERROR` |
|
|
|------------------------|----------------------------------|--------------------------:|
|
|
| Without the fix | about 200 MB per 15 s | 1 |
|
|
| With `clearAllMocks()` | about 45 MB per 15 s | 0 |
|
|
|
|
## Follow-up PRs
|
|
|
|
- A heap limit for the minions that fits, measured with a full run now that the mocks are released (see PR#135).
|