Skip to content

Commit 360ed75

Browse files
authored
Mix test parameters into the per-test :rand seed (#15869)
ExUnit seeds each test process with {phash2(module), phash2(name), seed}. parameterize re-runs the module per parameter set without changing the test name, so every parameter set of a test started from the same :rand state and drew the same random sequence. Hash the parameters together with the name when they are present. Tests without parameters keep their previous seed, so existing --seed reproductions are unaffected. Assisted-by: Claude Code:claude-fable-5-1 Signed-off-by: Jechol Lee <mr.jechol@gmail.com>
1 parent 7f3b294 commit 360ed75

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

lib/ex_unit/lib/ex_unit/runner.ex

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,12 @@ defmodule ExUnit.Runner do
550550

551551
## Helpers
552552

553-
defp generate_test_seed(seed, %ExUnit.Test{module: module, name: name}, rand_algorithm) do
553+
# Parameterized tests share module and name, so mix the parameters into the seed
554+
# to give each parameter set its own random sequence. Tests without parameters keep
555+
# the seed they had before, so existing `--seed` reproductions are unaffected.
556+
defp generate_test_seed(seed, %ExUnit.Test{} = test, rand_algorithm) do
557+
%ExUnit.Test{module: module, name: name, parameters: parameters} = test
558+
name = if parameters == %{}, do: name, else: {name, parameters}
554559
:rand.seed(rand_algorithm, {:erlang.phash2(module), :erlang.phash2(name), seed})
555560
end
556561

lib/ex_unit/test/ex_unit_test.exs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,27 @@ defmodule ExUnitTest do
717717
assert_receive {:tmp_dir, tmp_dir2} when tmp_dir1 != tmp_dir2
718718
end
719719

720+
test "parameterized tests get a different seed for each parameter set" do
721+
Process.register(self(), :parameterized_seed_tests)
722+
723+
defmodule ParameterizedSeedTests do
724+
use ExUnit.Case, async: true, parameterize: [%{value: 1}, %{value: 2}]
725+
726+
test "hello world" do
727+
send(:parameterized_seed_tests, {:rand, :rand.uniform(1_000_000)})
728+
end
729+
end
730+
731+
configure_and_reload_on_exit(seed: 1)
732+
733+
capture_io(fn ->
734+
assert ExUnit.run() == %{failures: 0, skipped: 0, total: 2, excluded: 0}
735+
end)
736+
737+
assert_receive {:rand, rand1}
738+
assert_receive {:rand, rand2} when rand1 != rand2
739+
end
740+
720741
test "empty parameterized tests" do
721742
defmodule EmptyParameterizedTests do
722743
use ExUnit.Case, async: true, parameterize: []

0 commit comments

Comments
 (0)