AutoFixture:如何使用通用的测试数据集来生成不同的对象?

AutoFixture: How to use a common test data set for the generation of different objects?

我想测试一个自己编写的 XML 解析器,它接受一个 XML 字符串和 returns 它的模型表示。

T Parse(string content);

我遇到的问题是关于我测试的断言部分。因为每次我调用 Create<T>() 它都会生成新的随机数据,在那种情况下这不是我想要的。我需要一个可以按以下顺序使用的通用测试数据集:

a) 生成 XML 可以传递给我的解析器的字符串

b) 使用相同的测试数据集生成模型表示

c) 将 XML 解析器结果与生成的模型表示和 Assert.AreEqual()

进行比较

我遇到了 Freeze<T>() 方法,"sounds" 喜欢它可以满足我的目的。但是我不知道如何使用它。

所以问题是:如何使用通用的测试数据集来生成不同的对象?

这是我目前的方法和静态测试数据生成器class。

public static class TestDataGenerator
{
    public static string GenerateSyntheticXmlTestData<T>(int minOid, int maxOid, int amount = 5)
    {
        var fixture = new Fixture()
        {
            RepeatCount = amount
        };

        fixture.Customizations.Add(new OidGenerator(minOid, maxOid));
        fixture.Customizations.Add(new EnableAllProperties());

        var testData = fixture.Create<T>();

        var serializedXmlTestData = XmlSerializerHelper.Current.SerializeToXmlDocument(testData, Encoding.UTF8);

        return serializedXmlTestData;
    }

    public static ICollection<T> GenerateSyntheticModelTestData<T>(int minOid, int maxOid, int amount = 1)
    {
        var fixture = new Fixture()
        {
            RepeatCount = 1
        };

        fixture.Customizations.Add(new OidGenerator(minOid, maxOid));

        var testData = fixture.CreateMany<T>(amount).ToList();

        return testData;
    }
}

这就是我想测试解析器的方式。我希望它清楚我想要实现的目标。

[Fact]
public void ShouldParse()
{
    // [...]
    var xmlContent = TestDataGenerator.GenerateSyntheticXmlTestData<MyType>(minOid: 1, maxOid: 100, amount: 5);

    // Here I would like to generate a model object using the same data
    //
    // var modelContent = new Fixture().Create<ModelType>(); 

    var parsedContent = parser.Parse(xmlContent);

    //parsedContent.Should().BeEquivalentTo(modelContet); 
}

我不是 100% 确定这是否是您要找的东西,但也许可以选择为您的 XML-数据创建具有自定义类型的自定义夹具?

public class CustomFixture : Fixture
{
    Customize<YourXmlType>(c => c.Without(f => f.XmlStringThatShouldNotBeGenerated));
    Customize<YourXmlType>(c => c.Do(f => f.XmlStringThatShouldNotBeGenerated = "Your shared xml string"));
}

这也可以与 c.With 一起使用,而不是 Without 和 Do,但是我前段时间在一个项目中遇到了问题,上面的解决方案对我来说更可靠。

在测试解析器时,我经常发现从基于 属性 的测试手册 中获取一页是最简单的。许多对基于 属性 的测试有用的技术对 AutoFixture 也很有用。

在对解析逻辑进行基于 属性 的测试时,定义与解析器一起使用的序列化程序通常很有用。即,可以将给定模型转换为解析器解析的格式的函数。在这种情况下,它将是一个 XML 序列化程序。

指示 AutoFixture(或基于 属性 的测试库)创建 'model' 对象的有效实例,而不是指示它生成有效的 XML 字符串,通常要容易得多.

设置 AutoFixture 来执行此操作后,让它创建模型的实例,然后序列化模型,并让解析器解析序​​列化的模型。断言是解析模型应该等于输入模型。

Scott Wlaschin 使用 FsCheck 称此测试模式为 There and back again. You can also see an example of it on my blog

使用 AutoFixture,它可能看起来像这样:

[Fact]
public void RoundTrippingWorks()
{
    var fixture = new Fixture().Customize(/*...*/);
    var model = fixture.Create<MyModel>();

    string xml = MyXmlSerializer.Serialize(model);
    MyModel actual = MyXmlParser.Parse(xml);

    Assert.Equal(model, actual);
}

(我没试过编译,所以可能有错别字...)