Skip to content

Conversation

@CyberiaResurrection
Copy link
Collaborator

@CyberiaResurrection CyberiaResurrection commented Aug 16, 2025

Order-randomising pytest plugins, while brilliant for their intended purpose of rumbling hidden test dependencies, are near-pessimal for mutmut's use case.

For a given mutation run, a subset of tests, ordered by increasing runtime, are fed to pytest. The idea is to run fast tests first, increasing the chances of bailing out quickly with a test failure on the mutant being tested.

Randomising the order of such tests destroys that ordering and thus blows out mutation test times, sometimes to the point of mutants that would normally be killed ending up timing out instead.

As such, pass parms to the pytest runner when testing a mutant to disable two common order-randomisation plugins, pytest-randomly and pytest-random-order.

For projects that use neither of those plugins, this PR is a no-op. As the checks show, pytest will gladly run without the to-be-disabled plugins even installed.

For example, running over 11 ObjectStatistics.__init__ mutants in PyRoute - which has ~800-odd distinct tests in its test suite and uses the pytest-randomly plugin:

Status quo:

Running mutation testing
⠼ 11/30133  🎉 1 🫥 0  ⏰ 10  🤔 0  🙁 0  🔇 0
0.12 mutations/second

Mutant results
--------------
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_1
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_10
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_11
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_12
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_13
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_14
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_15
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_16
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_17
⏰ PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_18
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_19

Process finished with exit code 0

With this change:

Running mutation testing
⠼ 11/30133  🎉 11 🫥 0  ⏰ 0  🤔 0  🙁 0  🔇 0
18.33 mutations/second

Mutant results
--------------
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_1
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_10
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_11
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_12
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_13
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_14
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_15
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_16
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_17
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_18
🎉 PyRoute.StatCalculation.ObjectStatistics.xǁObjectStatisticsǁ__init____mutmut_19

Process finished with exit code 0

That's something, assuming I haven't mucked up the numbers, like a 150x speedup simply by avoiding the test order randomisation. With the exact same tests and mutants in each case.

Addresses #423 .

Order-randomising pytest plugins, while brilliant for their
intended purpose of rumbling hidden test dependencies, are
near-pessimal for mutmut's use case.

For a given mutation run, a subset of tests, ordered by increasing
runtime, are fed to pytest.  The idea is to run fast tests first,
increasing the chances of bailing out quickly with a test failure
on the mutant being tested.

Randomising the order of such tests destroys that ordering and
thus blows out mutation test times, sometimes to the point of
mutants that would normally be killed ending up timing out
instead.

As such, pass parms to the pytest runner when testing a mutant
to disable two common order-randomisation plugins,
pytest-randomly and pytest-random-order.
@CyberiaResurrection
Copy link
Collaborator Author

@boxed , @Otto-AA , any comments/reviews?

@boxed
Copy link
Owner

boxed commented Aug 18, 2025

If you don't have these plugins that command will just be ignored?

@CyberiaResurrection
Copy link
Collaborator Author

@boxed - yes, as you can see in the CI checks - if neither random-order or randomly are installed, they'll be ignored.

@boxed boxed merged commit e33f6f7 into boxed:main Aug 18, 2025
5 checks passed
@boxed
Copy link
Owner

boxed commented Aug 18, 2025

Great! Thanks!

@CyberiaResurrection CyberiaResurrection deleted the DisableCommonOrderRandomisationPlugins branch August 18, 2025 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants