Skip to content

Commit 04e6817

Browse files
committed
feature: add the option --randomly-seed-per-test to use a different seed for each test
1 parent c2e8e2f commit 04e6817

File tree

4 files changed

+59
-11
lines changed

4 files changed

+59
-11
lines changed

CHANGELOG.rst

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
Changelog
33
=========
44

5+
* Add the option ``--randomly-seed-per-test`` to use a different seed for each test.
6+
7+
Resolves `Issue #600 <https://github.com/pytest-dev/pytest-randomly/issues/600>`__
8+
59
3.15.0 (2023-08-15)
610
-------------------
711

README.rst

+3
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ You can disable behaviours you don't like with the following flags:
160160
the start of every test
161161
* ``--randomly-dont-reorganize`` - turn off the shuffling of the order of tests
162162

163+
By default each test starts out with the same seed, if you'd like a different one
164+
per test you can use the ``--randomly-seed-per-test`` flag.
165+
163166
The plugin appears to Pytest with the name 'randomly'. To disable it
164167
altogether, you can use the ``-p`` argument, for example:
165168

src/pytest_randomly/__init__.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,15 @@ def pytest_addoption(parser: Parser) -> None:
111111
default=True,
112112
help="Stop pytest-randomly from randomly reorganizing the test order.",
113113
)
114+
group._addoption(
115+
"--randomly-dont-seed-per-test",
116+
action="store_false",
117+
dest="randomly_seed_per_test",
118+
default=True,
119+
help="""Use a different seed for each test. Can be helpful for getting
120+
different random data in each test, but still having reproducible
121+
tests. Default behaviour: False.""",
122+
)
114123

115124

116125
def pytest_configure(config: Config) -> None:
@@ -209,9 +218,17 @@ def pytest_runtest_setup(item: Item) -> None:
209218
_reseed(item.config, -1)
210219

211220

221+
def seed_from_string(string: str) -> int:
222+
return int(hashlib.md5(string.encode()).hexdigest(), 16)
223+
224+
212225
def pytest_runtest_call(item: Item) -> None:
213226
if item.config.getoption("randomly_reset_seed"):
214-
_reseed(item.config)
227+
if item.config.getoption("randomly_seed_per_test"):
228+
test_offset = seed_from_string(item.nodeid) + 100
229+
else:
230+
test_offset = 0
231+
_reseed(item.config, offset=test_offset)
215232

216233

217234
def pytest_runtest_teardown(item: Item) -> None:

tests/test_pytest_randomly.py

+34-10
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,31 @@ def test_b():
7878
assert test_b.num == test_a.num
7979
"""
8080
)
81-
out = ourtester.runpytest("--randomly-dont-reorganize")
81+
out = ourtester.runpytest("--randomly-dont-reorganize", "--randomly-dont-seed-per-test")
82+
out.assert_outcomes(passed=2, failed=0)
83+
84+
85+
def test_it_uses_different_random_seed_per_test(ourtester):
86+
"""
87+
Run a pair of tests that generate a number and assert they produce
88+
different numbers.
89+
"""
90+
ourtester.makepyfile(
91+
test_one="""
92+
import random
93+
94+
def test_a():
95+
test_a.num = random.random()
96+
if hasattr(test_b, 'num'):
97+
assert test_a.num != test_b.num
98+
99+
def test_b():
100+
test_b.num = random.random()
101+
if hasattr(test_a, 'num'):
102+
assert test_b.num != test_a.num
103+
"""
104+
)
105+
out = ourtester.runpytest()
82106
out.assert_outcomes(passed=2, failed=0)
83107

84108

@@ -601,7 +625,7 @@ def test_two(myfixture):
601625
assert random.getstate() == state_at_seed_two
602626
"""
603627
)
604-
args = ["--randomly-seed=2"]
628+
args = ["--randomly-seed=2", "--randomly-dont-seed-per-test"]
605629

606630
out = ourtester.runpytest(*args)
607631
out.assert_outcomes(passed=2)
@@ -633,7 +657,7 @@ def test_b():
633657
"""
634658
)
635659

636-
out = ourtester.runpytest("--randomly-seed=1")
660+
out = ourtester.runpytest("--randomly-seed=1", "--randomly-dont-seed-per-test")
637661
out.assert_outcomes(passed=2)
638662

639663

@@ -645,10 +669,10 @@ def test_faker(ourtester):
645669
fake = Faker()
646670
647671
def test_one():
648-
assert fake.name() == 'Ryan Gallagher'
672+
assert fake.name() == 'Justin Richard'
649673
650674
def test_two():
651-
assert fake.name() == 'Ryan Gallagher'
675+
assert fake.name() == 'Tiffany Williams'
652676
"""
653677
)
654678

@@ -692,7 +716,7 @@ def test_b():
692716
"""
693717
)
694718

695-
out = ourtester.runpytest("--randomly-seed=1")
719+
out = ourtester.runpytest("--randomly-seed=1", "--randomly-dont-seed-per-test")
696720
out.assert_outcomes(passed=2)
697721

698722

@@ -702,10 +726,10 @@ def test_numpy(ourtester):
702726
import numpy as np
703727
704728
def test_one():
705-
assert np.random.rand() == 0.417022004702574
729+
assert np.random.rand() == 0.46479378116435255
706730
707731
def test_two():
708-
assert np.random.rand() == 0.417022004702574
732+
assert np.random.rand() == 0.6413112443155088
709733
"""
710734
)
711735

@@ -765,7 +789,7 @@ def fake_entry_points(*, group):
765789
entry_points.append(_FakeEntryPoint("test_seeder", reseed))
766790

767791
# Need to run in-process so that monkeypatching works
768-
pytester.runpytest_inprocess("--randomly-seed=1")
792+
pytester.runpytest_inprocess("--randomly-seed=1", "--randomly-dont-seed-per-test")
769793
assert reseed.mock_calls == [
770794
mock.call(1),
771795
mock.call(1),
@@ -775,7 +799,7 @@ def fake_entry_points(*, group):
775799
]
776800

777801
reseed.mock_calls[:] = []
778-
pytester.runpytest_inprocess("--randomly-seed=424242")
802+
pytester.runpytest_inprocess("--randomly-seed=424242", "--randomly-dont-seed-per-test")
779803
assert reseed.mock_calls == [
780804
mock.call(424242),
781805
mock.call(424242),

0 commit comments

Comments
 (0)