| name | test-isolation |
| description | Rules for test isolation — preventing global state leakage from @eval monkeypatching |
Test Isolation Rules
Mycelia's test suite runs all files in a single Julia process via
include_all_tests. Global state mutations persist across files.
@eval Monkeypatching Convention
NEVER use @eval Mycelia to redefine module functions in test files unless
both of the following are true:
- The file uses a
zz_ prefix (e.g., zz_simulation_wrappers.jl) so it runs
last in alphabetical include order
- The redefined functions are not exercised by any subsequent test file
Preferred Alternatives (in order)
- Fake CONDA_RUNNER scripts — write a shell stub to a temp dir, override
Mycelia.CONDA_RUNNER path (see with_fake_conda_runner pattern)
- Dependency injection — accept a callable parameter that defaults to the
real implementation
- Environment variable gating — skip the code path when
MYCELIA_RUN_EXTERNAL=false
- Save/restore pattern — if
@eval is unavoidable:
Test.@testset "Guarded monkeypatch" begin
original = Mycelia.some_function
try
@eval Mycelia function some_function(args...)
# stub
end
# ... test code ...
finally
@eval Mycelia some_function = $original
end
end
Why This Matters
@eval Mycelia permanently replaces methods in the module's method table for
the entire process lifetime. Files sorted alphabetically after the patching file
will silently get the stub instead of the real function. This was found in ~12
of 34 PRs during a comprehensive audit (2026-04-04), causing:
- Tests passing with stubs instead of real implementations (false coverage)
- Integration tests silently disabled when
MYCELIA_RUN_EXTERNAL=true
- Non-deterministic failures depending on file include order
Checklist for PR Authors