【问题标题】:Dynamic assertions based on [InlineData] or [MemberData]基于 [InlineData] 或 [MemberData] 的动态断言
【发布时间】:2019-10-31 16:39:43
【问题描述】:

目前我们正在开发一些更大的组件测试。这些依赖于一些相当复杂的数据生成器,它们正在创建一些模板的数据基础。

问题

在使用 xUnit 进行测试时,我们在 [InlineData] 的帮助下进行测试:

[Theory]
[InlineData(Device.Type1, DeviceConfig.VeryHotDevice)]
[InlineData(Device.Type1, DeviceConfig.VeryColdDevice)]
public async Task TestCalculation(string deviceType, DeviceConfig deviceConfig)

我遇到的问题是关于不同DeviceConfig 值的断言。我必须创建一个switch-statement,其中包含测试方法中的每一个案例:

switch (simConfig)
{
    case DeviceConfig.VeryHotDevice:
        AssertVeryHotDevice(resultData);
    break;
    case DeviceConfig.VeryColdDevice:
        AssertVeryVoldDevice(resultData);
    break;
}

我想要的是类似下面的东西。所以基本上只是在测试方法中传递带有特定[InlineData] 和.Invoke 的断言的方法。

[Theory]
[InlineData(Device.Type1, DeviceConfig.VeryHotDevice, )]
[InlineData(Device.Type1, DeviceConfig.VeryColdDevice)]
public async Task TestCalculation(string deviceType, DeviceConfig deviceConfig, Action<ResultData> assertAction)

在没有详细说明的情况下,我已经通过在这个答案 (Lambda expression as inline data in xUnit) 的帮助下将 [InlineData] 更改为 [MemberData] 来完成这项工作。

遗憾的是,您随后遇到了以下问题 (MemberData tests show up as one test instead of many),因为 Actions 不是 IXunitSerializable。

对我来说,只显示一个测试(实际上是三到四个)既不可接受,也不能实现一些不方便的杂乱代码来支持IXunitSerializable。

问题

您如何保持此处的断言具有某种动态性,并直接将它们与测试数据一起传递,以保持测试方法的清洁?当然,如果您提出完全不同的建议,我也会很高兴。

【问题讨论】:

  • 如果有 3 或 4 个测试并且它们每个都使用不同的断言方法,那么你有什么理由不只是有 3 或 4 个单独的测试?否则,我认为您似乎已经包含了所有选项,我不确定还有什么要添加的。
  • 是的,我会考虑每个配置的单独测试方法。因为似乎每个配置都有自己的断言逻辑,这些断言逻辑可能独立于其他配置正确或不正确。所以在我看来,语义上这些是不同的测试,而不是一种不同的测试。

标签: c# .net-core xunit


【解决方案1】:

如果你想让你的测试出现,恐怕你必须支持IXunitSerializable。但这不符合它必须变得“混乱”。我们正在使用基于ClassData 和IXunitSerializable 的相当简单的方法。

下面的 sn-p 演示了我们如何编写测试。在我看来,这很清楚,也很合理。 我们使用继承自ClassDataCollection&lt;T&gt; 的类型,用ClassData 属性装饰我们的方法。这个类必须实现GetData() 方法,我们返回T 的实例。 这是必要的,因为为了支持ClassData,我们必须实现IEnumerable&lt;object[]&gt;。基类负责为返回的每个项目创建一个对象[1] GetData().

要单独显示测试,我们需要在 TestItem 类上实现 IXunitSerializable。在下面的代码示例中,TestItem 继承自 ClassDataItem 类。这个类实现了IXunitSerializable,它只是使用JSon来获取或添加值到IXunitSerializationInfo。

这是测试的样子:

       [Theory]
       [ClassData(typeof(RunTestWithMemberDataCollection))]
       public async Task RunTestWithMemberData(RunTestWithMemberDataCollection.TestItem data)
       {
       }

       public class RunTestWithMemberDataCollection :
           ClassDataCollection<RunTestWithMemberDataCollection.TestItem>
       {
           public override IEnumerable<TestItem> GetData()
           {
               yield return new TestItem { Prop1 = "", Prop2 = "data 1" };
               yield return new TestItem { Prop1 = "", Prop2 = "data 2" };
           }

           public class TestItem : ClassDataItem
           {
               public string Prop1 { get; set; }

               public string Prop2 { get; set; }
           }
       }

这里是你只需要一次的支持代码:

    public abstract class ClassDataCollection<T> : IEnumerable<object[]>
        where T : ClassDataItem
    {
        public IEnumerator<object[]> GetEnumerator()
        {
            return GetData()
                .Select(d => new object[] { (IXunitSerializable)d })
                .GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public abstract IEnumerable<T> GetData();
    }

    public class ClassDataItem : IXunitSerializable
    {
        public void Deserialize(IXunitSerializationInfo info)
        {
            JsonConvert.PopulateObject(info.GetValue<string>("TestItem"), this);
        }

        public void Serialize(IXunitSerializationInfo info)
        {
            info.AddValue("TestItem", JsonConvert.SerializeObject(this));
        }
    }

【讨论】:

    猜你喜欢
    • 2022-01-01
    • 2011-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    • 2023-03-04
    • 2012-08-08
    • 2019-01-01
    相关资源
    最近更新 更多