Summary
When solution is set in stryker-config.json, test-projects has no effect and is never validated. A test-projects entry pointing at a file that does not exist produces no error, no warning, and no log line — Stryker silently discovers and runs every test project in the solution instead.
The operating-modes docs say solution mode analyses the whole solution, so the "runs everything" half may well be intended. The half that looks unambiguously wrong is that an invalid test-projects path is accepted in silence. That is what makes the first half undiscoverable: the config looks like it is scoping the run, and there is no signal anywhere that it is inert.
Closest existing report I could find is discussion #2815, which hits the same wall but does not cover the silent acceptance of a non-existent path.
Minimal repro
A solution with one source project and two test projects — UnitTests (2 tests) and OtherTests (3 tests), both referencing Calc:
Repro.slnx
src/Calc/Calc.csproj
tests/UnitTests/UnitTests.csproj # 2 tests
tests/OtherTests/OtherTests.csproj # 3 tests
Case A — scope to one test project:
{ "stryker-config": {
"project": "Calc.csproj",
"solution": "Repro.slnx",
"test-projects": ["tests/UnitTests/UnitTests.csproj"]
} }
[INF] Number of tests found: 5 ...
Expected 2. Both test projects ran.
Case B — point test-projects at a path that does not exist:
{ "stryker-config": {
"project": "Calc.csproj",
"solution": "Repro.slnx",
"test-projects": ["tests/DoesNotExist/Nope.csproj"]
} }
[INF] Number of tests found: 5 ...
No error. No warning. Nothing in the log mentions the missing project at all.
Case C — the documented workaround, run from the test project directory with no solution key:
[INF] Analyzing 1 test project(s).
[INF] Number of tests found: 2 ...
Correct. So the scoping itself works; it is specifically inert in solution mode.
I reproduced the same behaviour with both "test-runner": "vstest" and "mtp".
Actual vs expected
|
actual |
expected |
valid test-projects + solution |
silently ignored |
honoured, or a warning that it is ignored in solution mode |
non-existent test-projects path |
silently ignored, exit 0 |
error, or at minimum a warning |
Why this is worth fixing
On a real project this cost us roughly 20 minutes of CI wall-clock per run for weeks before anyone found the cause. test-projects named only the unit test suite, so the config read as correctly scoped. In fact the solution's integration test project was also being run — against every mutant — and each of those tests starts a PostgreSQL container via Testcontainers. That worked out to ~11s per mutant against a unit suite that runs in 209 ms, and it pushed the pipeline past its 30-minute job ceiling.
Nothing in the output suggested the config was being disregarded. The only reason we found it was noticing database containers churning during a mutation run. Switching to project mode dropped the step from 23m16s to about 30s.
Suggested fix
Either would have surfaced this immediately:
- Validate
test-projects regardless of mode — error on an entry that does not resolve to an existing project file. This seems worth doing independently of anything else.
- Warn when
test-projects is set but inert, e.g. test-projects is ignored in solution mode; all test projects in the solution will be run.
Honouring test-projects as a filter within solution mode would be the most useful outcome, but a diagnostic alone would have saved all of the above.
Environment
- Stryker.NET 4.16.0
- .NET SDK 10.0.400
- macOS (Darwin 25.5.0), also observed on
ubuntu-24.04 GitHub-hosted runners
- xunit, both
vstest and mtp test runners
Summary
When
solutionis set instryker-config.json,test-projectshas no effect and is never validated. Atest-projectsentry pointing at a file that does not exist produces no error, no warning, and no log line — Stryker silently discovers and runs every test project in the solution instead.The operating-modes docs say solution mode analyses the whole solution, so the "runs everything" half may well be intended. The half that looks unambiguously wrong is that an invalid
test-projectspath is accepted in silence. That is what makes the first half undiscoverable: the config looks like it is scoping the run, and there is no signal anywhere that it is inert.Closest existing report I could find is discussion #2815, which hits the same wall but does not cover the silent acceptance of a non-existent path.
Minimal repro
A solution with one source project and two test projects —
UnitTests(2 tests) andOtherTests(3 tests), both referencingCalc:Case A — scope to one test project:
{ "stryker-config": { "project": "Calc.csproj", "solution": "Repro.slnx", "test-projects": ["tests/UnitTests/UnitTests.csproj"] } }Expected 2. Both test projects ran.
Case B — point
test-projectsat a path that does not exist:{ "stryker-config": { "project": "Calc.csproj", "solution": "Repro.slnx", "test-projects": ["tests/DoesNotExist/Nope.csproj"] } }No error. No warning. Nothing in the log mentions the missing project at all.
Case C — the documented workaround, run from the test project directory with no
solutionkey:Correct. So the scoping itself works; it is specifically inert in solution mode.
I reproduced the same behaviour with both
"test-runner": "vstest"and"mtp".Actual vs expected
test-projects+solutiontest-projectspathWhy this is worth fixing
On a real project this cost us roughly 20 minutes of CI wall-clock per run for weeks before anyone found the cause.
test-projectsnamed only the unit test suite, so the config read as correctly scoped. In fact the solution's integration test project was also being run — against every mutant — and each of those tests starts a PostgreSQL container via Testcontainers. That worked out to ~11s per mutant against a unit suite that runs in 209 ms, and it pushed the pipeline past its 30-minute job ceiling.Nothing in the output suggested the config was being disregarded. The only reason we found it was noticing database containers churning during a mutation run. Switching to project mode dropped the step from 23m16s to about 30s.
Suggested fix
Either would have surfaced this immediately:
test-projectsregardless of mode — error on an entry that does not resolve to an existing project file. This seems worth doing independently of anything else.test-projectsis set but inert, e.g.test-projects is ignored in solution mode; all test projects in the solution will be run.Honouring
test-projectsas a filter within solution mode would be the most useful outcome, but a diagnostic alone would have saved all of the above.Environment
ubuntu-24.04GitHub-hosted runnersvstestandmtptest runners