【问题标题】:How to write one unit test that handles different types?如何编写一个处理不同类型的单元测试?
【发布时间】:2019-09-17 15:17:06
【问题描述】:

我正在为我的 discord 机器人(使用 XUnit)编写测试,我想知道是否可以用一个测试替换我的测试。如果是这样,我该怎么做?

到目前为止,我为我的 Unity 类(它是 Unity Container、DI 框架的包装器)编写了 4 个单元测试。这些测试按预期工作,但每次我向容器添加新类型时添加一个新测试似乎并不正确。我查看了类似问题的答案,但解决方案要么复杂,要么对我的案例没有用处。

我正在测试的 Unity 类中的方法:

public static T Resolve<T>()
{
    return Container.Resolve<T>();
}

它从 Unity Container 中返回一个对应类型的实例。

测试:

[Fact]
public void ResolveIDataStorage_ShouldWork()
{
    var storage1 = Unity.Resolve<IDataStorage>();
    var storage2 = Unity.Resolve<IDataStorage>();

    Assert.NotNull(storage1);
    Assert.NotNull(storage2);
    Assert.Same(storage1, storage2);
}

[Fact]
public void ResolveILogger_ShouldWork()
{
    var logger1 = Unity.Resolve<ILogger>();
    var logger2 = Unity.Resolve<ILogger>();

    Assert.NotNull(logger1);
    Assert.NotNull(logger2);
    Assert.Same(logger1, logger2);
}

[Fact]
public void ResolveDiscordSocketClient_ShouldWork()
{
    var client1 = Unity.Resolve<DiscordSocketClient>();
    var client2 = Unity.Resolve<DiscordSocketClient>();

    Assert.NotNull(client1);
    Assert.NotNull(client2);
    Assert.Same(client1, client2);
}

[Fact]
public void ResolveConnection_ShouldWork()
{
    var con1 = Unity.Resolve<Connection>();
    var con2 = Unity.Resolve<Connection>();

    Assert.NotNull(con1);
    Assert.NotNull(con2);
    Assert.Same(con1, con2);
}

在每个测试中,我解析一些类型并断言两个对象不为空并且它们应该是同一个实例。基本上,这些断言应该适用于任何类型(或一组特定类型,可能是 [Theory] ​​测试的参数),因此,为避免复制粘贴,进行一次测试会非常方便。

【问题讨论】:

  • 您可以使用反射对所需成员进行泛型并调用它。从那里断言预期的行为。
  • 也许像this这样的参数化测试?

标签: c# unit-testing moq unity-container xunit


【解决方案1】:

如果您的目标只是为您想要测试的每种类型提供一个遵循相同模式的测试,您可以将测试提取到其自己的通用方法中,然后在单个测试中为每种类型调用该方法:

[Fact] 
public void Resolve_ShouldWork() 
{ 
    AssertResolvedTypesAreSame<IDataStorage>();
    AssertResolvedTypesAreSame<ILogger>();
    AssertResolvedTypesAreSame<DiscordSocketClient>();
    AssertResolvedTypesAreSame<Connection>();
}

private void AssertResolvedTypesAreSame<T>()
{
    var t1 = Unity.Resolve<T>(); 
    var t2 = Unity.Resolve<T>();

    Assert.NotNull(t1); 
    Assert.NotNull(t2); 
    Assert.Same(t1, t2); 
}

【讨论】:

  • 原来就是这么简单,谢谢。顺便说一句,我可以以某种方式将此测试转换为[Theory] 测试吗?
  • @Glitch 据我所知,理论上你不能做泛型。我建议了一个使用反射的选项。
  • 是的,XUnit 似乎没有为通用理论提供任何东西(在 GitHub 存储库 here 中有讨论)。正如@Nkosi 提到的,唯一的选择似乎包括反射。还有一个示例here 创建用于处理泛型参数的自定义属性
【解决方案2】:

使用反射来生成需要解析的泛型方法并调用它。

从那里断言预期的行为。

使用内联数据,您现在可以重复测试而无需复制代码。

以下示例基于已经提供的代码。

[Theory]
[InlineData(typeof(IDataStorage))]
[InlineData(typeof(ILogger))]
[InlineData(typeof(DiscordSocketClient))]
[InlineData(typeof(Connection))]
public void Resolve_Singleton_Services_ShouldWork(Type type) {
    //Arrange
    var unityType = typeof(Unity);
    var resolve = unityType.GetMethod("Resolve", BindingFlags.Static| BindingFlags.Public);
    var genericResolve = resolve?.MakeGenericMethod(type);

    //Act
    var instance1 = genericResolve?.Invoke(null, null); // Unity.Resolve<type>()
    var instance2 = genericResolve?.Invoke(null, null); // Unity.Resolve<type>()

    //Assert
    Assert.NotNull(instance1);
    Assert.NotNull(instance2);
    Assert.Same(instance1, instance2);
}

测试的安排部分假设容器的每个配置都已被调用。

【讨论】:

    猜你喜欢
    • 2019-12-22
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 2012-02-03
    • 2019-01-28
    • 1970-01-01
    相关资源
    最近更新 更多