【问题标题】:Autofixture: Issue when creating value typesAutofixture:创建值类型时的问题
【发布时间】:2016-06-21 01:29:37
【问题描述】:

使用 xUnit.net 调试以下代码的测试方法 Test1Test2 并放置断点和 CreateValueAndReferenceType() 的结尾,您会看到变量 valueType 在两次运行中是相同的,而变量referenceType 已更改。前者对我来说令人惊讶,也是一个问题(我添加了字符串类型的行只是为了完整性)。

public class MyFixture : Fixture
{
    public void CreateValueAndReferenceType()
    {
        var valueType = this.Create<int>();
        var referenceTye = this.Create<string>();
    }
}

public class TestClass1
{
    [Fact]
    public void Test1()
    {
        var myFixture = new MyFixture();
        myFixture.CreateValueAndReferenceType();
    }
}

public class TestClass2
{
    [Fact]
    public void Test2()
    {
        var myFixture = new MyFixture();
        myFixture.CreateValueAndReferenceType();
    }
}

【问题讨论】:

    标签: autofixture


    【解决方案1】:

    我认为,您所看到的是与 .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
    

    我认为由于并行性,Test1Test2 都是并行执行的,并且基本上在同一个时钟周期内。

    一种解决方法是将两个测试放在同一个测试类中:

    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
    

    请注意,除了一个测试运行之外,所有测试运行的数字都相同。

    【讨论】:

    • 解决方法可能是:this.Register(() => (byte)Guid.NewGuid().GetHashCode());
    • 更正我的建议:this.Register(() => Math.Max((byte)1, (byte)Guid.NewGuid().GetHashCode())) ;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-26
    • 2011-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多