Skip to content

Commit 946b247

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

File tree

4 files changed

+63
-11
lines changed

4 files changed

+63
-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

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

84110

@@ -601,7 +627,7 @@ def test_two(myfixture):
601627
assert random.getstate() == state_at_seed_two
602628
"""
603629
)
604-
args = ["--randomly-seed=2"]
630+
args = ["--randomly-seed=2", "--randomly-dont-seed-per-test"]
605631

606632
out = ourtester.runpytest(*args)
607633
out.assert_outcomes(passed=2)
@@ -633,7 +659,7 @@ def test_b():
633659
"""
634660
)
635661

636-
out = ourtester.runpytest("--randomly-seed=1")
662+
out = ourtester.runpytest("--randomly-seed=1", "--randomly-dont-seed-per-test")
637663
out.assert_outcomes(passed=2)
638664

639665

@@ -645,10 +671,10 @@ def test_faker(ourtester):
645671
fake = Faker()
646672
647673
def test_one():
648-
assert fake.name() == 'Ryan Gallagher'
674+
assert fake.name() == 'Justin Richard'
649675
650676
def test_two():
651-
assert fake.name() == 'Ryan Gallagher'
677+
assert fake.name() == 'Tiffany Williams'
652678
"""
653679
)
654680

@@ -692,7 +718,7 @@ def test_b():
692718
"""
693719
)
694720

695-
out = ourtester.runpytest("--randomly-seed=1")
721+
out = ourtester.runpytest("--randomly-seed=1", "--randomly-dont-seed-per-test")
696722
out.assert_outcomes(passed=2)
697723

698724

@@ -702,10 +728,10 @@ def test_numpy(ourtester):
702728
import numpy as np
703729
704730
def test_one():
705-
assert np.random.rand() == 0.417022004702574
731+
assert np.random.rand() == 0.46479378116435255
706732
707733
def test_two():
708-
assert np.random.rand() == 0.417022004702574
734+
assert np.random.rand() == 0.6413112443155088
709735
"""
710736
)
711737

@@ -765,7 +791,7 @@ def fake_entry_points(*, group):
765791
entry_points.append(_FakeEntryPoint("test_seeder", reseed))
766792

767793
# Need to run in-process so that monkeypatching works
768-
pytester.runpytest_inprocess("--randomly-seed=1")
794+
pytester.runpytest_inprocess("--randomly-seed=1", "--randomly-dont-seed-per-test")
769795
assert reseed.mock_calls == [
770796
mock.call(1),
771797
mock.call(1),
@@ -775,7 +801,9 @@ def fake_entry_points(*, group):
775801
]
776802

777803
reseed.mock_calls[:] = []
778-
pytester.runpytest_inprocess("--randomly-seed=424242")
804+
pytester.runpytest_inprocess(
805+
"--randomly-seed=424242", "--randomly-dont-seed-per-test"
806+
)
779807
assert reseed.mock_calls == [
780808
mock.call(424242),
781809
mock.call(424242),

0 commit comments

Comments
 (0)