编程语言
首页 > 编程语言> > c# – 来自xunit MemberData函数的静态数据计算两次

c# – 来自xunit MemberData函数的静态数据计算两次

作者:互联网

我在计算两次C#Xunit测试中来自静态类的计算数据时遇到了一些麻烦.

这将用于实际的生产代码要复杂得多,但随后的代码足以展示我所看到的问题.

在下面的代码中,我有一个随机生成的,延迟加载的int在当前时间播种.

我在这里测试的是这个属性等于它自己.我通过MemberData函数将属性的值插入到测试中.

由于该属性应该只被初始化一次,我希望这个测试应该总是通过.我希望在运行RandomIntMemberData函数时会初始化静态字段,而不会再次.

但是,此测试始终失败.插入测试的值和测试的值总是不同的.

此外,如果我调试,我只看到初始化代码被命中一次.也就是说,正在测试的值.我从来没有看到正在测试的值的初始化.

我是在误解某些东西,还是Xunit做了一些奇怪的幕后魔术设置它的输入数据,然后在测试实际运行时再次初始化值?

重现错误的最小代码

public static class TestRandoIntStaticClass
{
    private static readonly Lazy<int> LazyRandomInt = new Lazy<int>(() =>
    {
        // lazily initialize a random interger seeded off of the current time
        // according to readings, this should happen only once
        return new Random((int) DateTime.Now.Ticks).Next();
    });

    // according to readings, this should be a thread safe operation
    public static int RandomInt => LazyRandomInt.Value; 
}

考试

public class TestClass
{
    public static IEnumerable<object[]> RandomIntMemberData()
    {
        var randomInt = new List<object[]>
        {
            new object[] {TestRandoIntStaticClass.RandomInt},
        };

        return randomInt as IEnumerable<object[]>;
    }

    [Theory]
    [MemberData(nameof(RandomIntMemberData))]
    public void RandoTest(int rando)
    {
        // these two ought to be equal if TestRandoIntStaticClass.RandomInt is only initialized once 
        Assert.True(rando == TestRandoIntStaticClass.RandomInt,
                    $"{nameof(rando)} = {rando} but {nameof(TestRandoIntStaticClass.RandomInt)} = {TestRandoIntStaticClass.RandomInt}");
    }
}

解决方法:

在测试发现时,Visual Studio Xunit控制台创建AppDomain,其中包含所有属性的测试数据,如MemberData,ClassData,DataAttribute,因此所有数据在构建后都只保存在内存中(这也是XUnit要求类可序列化的原因).

我们可以通过向您的方法添加一个简单的记录器来验证这一点:

namespace XUnitTestProject1
{
    public class TestClass
    {
        public static IEnumerable<object[]> RandomIntMemberData()
        {
            var randomInt = new List<object[]>
            {
                new object[]
                    {TestRandoIntStaticClass.RandomInt},
            };
            return randomInt;
        }

        [Theory]
        [MemberData(nameof(RandomIntMemberData))]
        public void RandoTest(int rando)
        {
            // these two ought to be equal if TestRandoIntStaticClass.RandomInt is only initialized once 
            Assert.True(rando == TestRandoIntStaticClass.RandomInt, $"{nameof(rando)} = {rando} but {nameof(TestRandoIntStaticClass.RandomInt)} = {TestRandoIntStaticClass.RandomInt}");
        }

    }

    public static class TestRandoIntStaticClass
    {
        private static readonly Lazy<int> LazyRandomInt = new Lazy<int>(() =>
        {   // lazily initialize a random interger seeded off of the current time
            // according to readings, this should happen only once
            var randomValue = new Random((int) DateTime.Now.Ticks).Next();

            File.AppendAllText(@"D:\var\log.txt", $"Call TestRandoIntStaticClass {randomValue}; ThreadId {Thread.CurrentThread.ManagedThreadId} " + Environment.NewLine);
            return randomValue;
        });

        public static int RandomInt => LazyRandomInt.Value; // according to readings, this should be a thread safe operation
    }
}

结果我们在日志中看到:

> Call TestRandoIntStaticClass 1846311153; ThreadId 11  
> Call TestRandoIntStaticClass 1007825738; ThreadId 14

并在测试执行结果

rando = 1846311153 but RandomInt = 1007825738
Expected: True
Actual:   False
   at 

但是,如果你将使用dotnet测试将会成功,因为’数据生成’和测试运行将在一个进程上启动

标签:c,xunit,xunit-net
来源: https://codeday.me/bug/20190522/1152568.html