autofixture - 在 Autofixture 中每次测试运行时生成相同的“随机”值
问题描述
我想使用 Autofixture 创建具有随机值的(复杂)类型。只要所有值都不同,我不关心创建数据的价值。我想让Autofixture在每次测试运行时创建相同的数据。
默认情况下,使用 Autofixture 的数据可能会导致测试有时通过有时失败的不稳定测试。
让它更清楚一点。这是我正在寻找的行为的一个例子。这也应该适用于复杂类型,但我没有在示例中实现。
[Fact]
public void AlwaysTheSameValues()
{
var semiRandomValues = new SemiRandomValues();
int value1 = semiRandomValues.GetRandomInt(); // Always 145
int value2 = semiRandomValues.GetRandomInt(); // Always 163
int value3 = semiRandomValues.GetRandomInt(); // Always 153
string value4 = semiRandomValues.GetRandomString(); // Always "NFNWLYGHLPLYAVYQHUVY"
string value5 = semiRandomValues.GetRandomString(); // Always "ARNXRNCELHYQTAJIXMRC"
Assert.True(value2>50); // This wil never fail
}
[Fact]
public void DifferentValuesEachRun()
{
var semiRandomValues = new Fixture();
int value1 = semiRandomValues.Create<int>(); // Produces a different result in each run
int value2 = semiRandomValues.Create<int>(); // Produces a different result in each run
int value3 = semiRandomValues.Create<int>(); // Produces a different result in each run
string value4 = semiRandomValues.Create<string>(); // Produces a different result in each run
string value5 = semiRandomValues.Create<string>(); // Produces a different result in each run
Assert.True(value2 > 50); // This wil sometimes fail
}
使用这个类
public class SemiRandomValues
{
private readonly Random _random = new Random(0); // The 0 makes all value the same each run
public int GetRandomInt()
{
return _random.Next(1, 200);
}
public string GetRandomString()
{
var chars = Enumerable
.Range(1, 20)
.Select(_ => (char)_random.Next(65, 90))
.ToArray();
return new string(chars);
}
}
注意:我不想依赖随机值(故意),但使用默认的 Autofixture 行为,我可以(意外地)检查未失败的工作单元测试。之后,当同事稍后运行该测试时,它可能会失败。对于那个同事来说,也很难重现失败的测试,因为测试可以在以后成功。
解决方案
我做了一个例子来说明几种方法。一种方法是使用 DataAnnotations,另一种方法是创建自定义项,以确保您获得对您的情况有意义的默认值。
马克·西曼十多年前就写过这篇文章。
void Main()
{
var fixture = new Fixture().Customize(new MySpecimenBuilder().ToCustomization());
for (int i = 0; i < 3; i++)
{
var foo = fixture.Create<Foo>();
Console.WriteLine($"{i} Bar: {foo.Bar}, Baz: {foo.Baz}, Qux: {foo.Qux}");
// Example output
//0 Bar: 42, Baz: ABCDE983, Qux: 42
//1 Bar: 42, Baz: ABCDE821, Qux: 42
//2 Bar: 42, Baz: ABCDE974, Qux: 42
}
}
public class Foo
{
public int Bar { get; set; }
[RegularExpression(@"ABCDE\d{3}")]
public string Baz { get; set; }
[Range(42, 42)]
public int Qux { get; set; }
}
public class MySpecimenBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
return request switch
{
PropertyInfo pi when pi.Name == nameof(Foo.Bar) => 42,
_ => new NoSpecimen()
};
}
}
推荐阅读
- python - 如何将 Pandas Dataframe 转换为 Keras RNN 以解决多变量分类问题
- api - 为什么我会收到错误消息:Android Gradle 插件仅支持 Kotlin Gradle 插件版本 1.3.10 及更高版本
- wordpress - 显示未登录用户的注册链接,而不是 Buddypress 活动循环
- php - 具有多个接口和继承的构造函数声明
- java - 在java中转换非结构化对象
- woocommerce - 产品库操作后的 WooCommerce
- identifier - 由于“未解析的标识符”错误,工作表视图结构崩溃
- c++ - 是否可以在 C++ 项目中模拟外部 C 库函数?
- ios - Swift Scale Animation 不是动画视图
- reactjs - 如何在反应中更新状态的对象数据