Я думаю, что вы видите основную проблему, связанную с генерацией псевдослучайных чисел в .NET (IIRC, другие платформы имеют аналогичные проблемы). По сути, System.Random
является детерминированным, но инициализируется случайным начальным числом, которое, среди прочего, зависит от текущего времени компьютера. Если вы создаете экземпляры Random
в тесном цикле, код выполняется быстрее, чем точность системных часов. Что-то вроде этого:
for (int i = 0; i < 10; i++)
Console.Write(new Random().Next(0, 9));
часто будет выводить такой вывод:
5555555555
Большинство значений в AutoFixture генерируются различными экземплярами Random
— исключение составляет тип string
, значения которого генерируются Guid.NewGuid().ToString()
.
Я думаю, что вы видите это из-за параллельного выполнения xUnit.net.
Чтобы точно определить проблему, я перефразировал проблему так, чтобы она не зависела от отладки или наследования:
public static class Reporter
{
public static void CreateValueAndReferenceType(
IFixture fixture,
ITestOutputHelper @out)
{
var valueType = fixture.Create<int>();
var referenceTye = fixture.Create<string>();
@out.WriteLine("valueType: {0}", valueType);
@out.WriteLine("referenceType: {0}", referenceTye);
}
}
public class TestClass1
{
private readonly ITestOutputHelper @out;
public TestClass1(ITestOutputHelper @out)
{
this.@out = @out;
}
[Fact]
public void Test1()
{
Reporter.CreateValueAndReferenceType(new Fixture(), this.@out);
}
}
public class TestClass2
{
private readonly ITestOutputHelper @out;
public TestClass2(ITestOutputHelper @out)
{
this.@out = @out;
}
[Fact]
public void Test2()
{
Reporter.CreateValueAndReferenceType(new Fixture(), this.@out);
}
}
Когда вы запустите это с помощью средства запуска консоли xUnit.net, вы увидите хорошо воспроизведенную проблему:
$ packages/xunit.runner.console.2.1.0/tools/xunit.console 37925109/bin/Debug/Ploeh.StackOverflow.Q37925109.dll -diagnostics
-parallel all
xUnit.net Console Runner (64-bit .NET 4.0.30319.42000)
Discovering: Ploeh.StackOverflow.Q37925109 (app domain = on [shadow copy], method display = ClassAndMethod)
Discovered: Ploeh.StackOverflow.Q37925109 (running 2 test cases)
Starting: Ploeh.StackOverflow.Q37925109 (parallel test collections = on, max threads = 4)
Ploeh.StackOverflow.Q37925109.TestClass2.Test2 [PASS]
Output:
valueType: 246
referenceType: cc39f570-046a-4a0a-8adf-ab7deadd0e26
Ploeh.StackOverflow.Q37925109.TestClass1.Test1 [PASS]
Output:
valueType: 246
referenceType: 87455351-03f7-4640-99fb-05af910da267
Finished: Ploeh.StackOverflow.Q37925109
=== TEST EXECUTION SUMMARY ===
Ploeh.StackOverflow.Q37925109 Total: 2, Errors: 0, Failed: 0, Skipped: 0, Time: 0,429s
В приведенном выше примере вы заметите, что я явно вызвал бегун с помощью -parallel all
, но мне не нужно было этого делать, так как это значение по умолчанию.
Если, с другой стороны, вы отключите распараллеливание с помощью -parallel none
, вы увидите, что значения другие:
$ packages/xunit.runner.console.2.1.0/tools/xunit.console 37925109/bin/Debug/Ploeh.StackOverflow.Q37925109.dll -diagnostics
-parallel none
xUnit.net Console Runner (64-bit .NET 4.0.30319.42000)
Discovering: Ploeh.StackOverflow.Q37925109 (app domain = on [shadow copy], method display = ClassAndMethod)
Discovered: Ploeh.StackOverflow.Q37925109 (running 2 test cases)
Starting: Ploeh.StackOverflow.Q37925109 (parallel test collections = off, max threads = 4)
Ploeh.StackOverflow.Q37925109.TestClass2.Test2 [PASS]
Output:
valueType: 203
referenceType: 1bc75a33-5542-4d9f-b42d-57ed85dc418d
Ploeh.StackOverflow.Q37925109.TestClass1.Test1 [PASS]
Output:
valueType: 117
referenceType: 6a508699-dc35-4bcd-8a7b-15eba64b24b4
Finished: Ploeh.StackOverflow.Q37925109
=== TEST EXECUTION SUMMARY ===
Ploeh.StackOverflow.Q37925109 Total: 2, Errors: 0, Failed: 0, Skipped: 0, Time: 0,348s
Я думаю, что из-за параллелизма и Test1
, и Test2
выполняются параллельно и, по сути, в одном и том же тике.
Один из обходных путей — поместить оба теста в один и тот же тестовый класс:
public class TestClass1
{
private readonly ITestOutputHelper @out;
public TestClass1(ITestOutputHelper @out)
{
this.@out = @out;
}
[Fact]
public void Test1()
{
Reporter.CreateValueAndReferenceType(new Fixture(), this.@out);
}
[Fact]
public void Test2()
{
Reporter.CreateValueAndReferenceType(new Fixture(), this.@out);
}
}
Это дает два разных целочисленных значения, потому что (IIRC) xUnit.net запускает только разные тестовые классы параллельно:
$ packages/xunit.runner.console.2.1.0/tools/xunit.console 37925109/bin/Debug/Ploeh.StackOverflow.Q37925109.dll -diagnostics
-parallel all
xUnit.net Console Runner (64-bit .NET 4.0.30319.42000)
Discovering: Ploeh.StackOverflow.Q37925109 (app domain = on [shadow copy], method display = ClassAndMethod)
Discovered: Ploeh.StackOverflow.Q37925109 (running 2 test cases)
Starting: Ploeh.StackOverflow.Q37925109 (parallel test collections = on, max threads = 4)
Ploeh.StackOverflow.Q37925109.TestClass1.Test2 [PASS]
Output:
valueType: 113
referenceType: e8c30ad8-f2c8-4767-9e9f-69b55c50e659
Ploeh.StackOverflow.Q37925109.TestClass1.Test1 [PASS]
Output:
valueType: 232
referenceType: 3eb60bf3-4d43-4a91-aef2-42f7e23e35b3
Finished: Ploeh.StackOverflow.Q37925109
=== TEST EXECUTION SUMMARY ===
Ploeh.StackOverflow.Q37925109 Total: 2, Errors: 0, Failed: 0, Skipped: 0, Time: 0,360s
Эта теория также подтверждается тем фактом, что если вы повторите эксперимент достаточное количество раз, вы увидите, что время от времени числа будут отличаться. Вот целочисленные результаты 25 тестовых прогонов:
33 33
92 92
211 211
13 13
9 9
160 160
55 55
155 155
137 137
161 161
242 242
183 183
237 237
151 151
104 104
254 254
123 123
244 244
144 144
223 9
196 196
126 126
199 199
221 221
132 132
Обратите внимание, что все тестовые прогоны, кроме одного, имеют одинаковые номера.
person
Mark Seemann
schedule
20.06.2016