在对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}");
}
}
答案 0 :(得分:4)
在发现测试时,Visual Studio Xunit控制台运行程序为所有属性(例如MemberData,ClassData,DataAttribute)创建带有测试数据的AppDomain,因此所有数据仅在构建后保存在内存中(这也是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 test
将会成功,因为“数据生成”和测试运行将在一个进程中启动