【问题标题】:Difficulties using InlineData in Unit Test - parameter is a controller在单元测试中使用 InlineData 的困难 - 参数是控制器
【发布时间】:2019-10-04 09:58:42
【问题描述】:

我是测试新手,现在正在学习使用 xUnit。

我已经编写了下面的测试,它可以工作并给出我正在寻找的结果,但我认为我应该能够使它更干净,就像在 nUnit 中使用 TestCase 时一样。因此,我尝试使用 InlineData,我理解的是 xUnits "TestCase",但我不知道该怎么做。

当尝试设置它时,如下所示(请参阅注释掉的行),然后它告诉我

    [Theory]
    // [InlineData("")] // wanna use InlineData just to make the code/test cleaner.
    // [InlineData(null)]
    public async Task Get_Return_Something(
        MyController sut)
    {
        var result1 = await sut.Get(""); // when placing "sut" as param, I get: cannot convert from MyController to string. 
        var result2 = await sut.Get(null); // same applies here..

        result1.ShouldBeOfType(typeof(OkObjectResult));
        result2.ShouldBeOfType(typeof(BadRequestObjectResult));
    }

我的问题是 - 如何实现 InlineData 以使测试更清晰。当我有一个控制器作为参数时,它甚至可能吗?

澄清一下 -> 是否有办法使用 nUnit 中的语法:

// something like this
[TestCase("", OkObjectResult)]
[TestCase(null, BadRequestObjectResult)]
public async Task Get_Return_Something(MyController sut, param1, param2)
{
    var result = await.sut.Get(param1);
    result.ShouldBeOfType(typeof(param2));
}

也许 nUnit 示例不正确 - 但我试图通过更简洁的代码来指出我在寻找什么。通过遵循 TestCase 逻辑,我可以轻松地将测试扩展到 10-15 个不同的 TestCase。而且我听说 xUnit 对 InlineData 做了类似的事情(LasseVågsætherKarlsen 告诉我这是不可能的)——然后是 MemberClass 或 ClassData。

【问题讨论】:

  • 您不能为此使用 InlineData,或者您需要指定类似字符串的内容,然后测试方法可用于构造控制器。属性不能将对象的引用作为参数,除了字符串等内置文字类型。
  • 您应该查看 MemberData 或 ClassData。
  • @LasseVågsætherKarlsen 谢谢。我可以看到,在尝试使用 ClassData 时,我必须将类更改为 IEnumerable(如果我理解正确的话) - 我不应该能够运行/编写测试而不必接触测试类之外的类?
  • 你可以添加MemberData,意思是你给你的测试类添加一个属性或方法来提供测试数据,这允许构造任何实例并返回它。
  • 你可以从不同的角度接近它。在测试方法中实例化控制器,但将测试逻辑提取到专用方法中。然后在测试中将控制器传递给该方法Assert_That_Return_Something(controller)。使用这种方法,您将拥有测试方法的描述性名称,描述测试用例的重要部分将在测试方法中(无需滚动或跳转)

标签: c# unit-testing xunit


【解决方案1】:

可能性 1

  1. 创建MyControllerClassData
    public class MyControllerClassData : IEnumerable<object[]>
    {
        public IEnumerator<object[]> GetEnumerator()
        {
            yield return new object[] {
                new MyController
                {
                  Id = 1,
                  // ...
                }
            };
        }
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
  1. 修改你的理论
    [Theory]
    [ClassData(typeof(MyControllerClassData))]
    public async Task Get_Return_Something(MyController sut)
    {
        var result1 = await sut.Get(""); // when placing "sut" as param, I get: cannot convert from MyController to string. 
        var result2 = await sut.Get(null); // same applies here..

        result1.ShouldBeOfType(typeof(OkObjectResult));
        result2.ShouldBeOfType(typeof(BadRequestObjectResult));
    }

可能性2

  1. 在构造函数中创建 MyController 类并将其存储在私有属性中
  2. 将参数更改为您想要的字符串和objectResult
  3. 始终使用相同的 MyController,使用字符串执行 Get() 并根据预期的 objectResult 对其进行验证。

此解决方案似乎更简洁一些,因为您对每种情况都有一个测试。

【讨论】:

  • 我的问题还不够清楚,因为第一种可能性并不比我已经拥有的更简单。我想做的是从 nUnit 实现 [Testcase(param 1)]-syntax,我有一个包含更多测试的测试用例,这样就不必编写 result1.Shouldbe... AND result2.shouldbe.. .但只写result.shouldbe..因为设置是一样的,但是testcase中的params是不同的。希望这是有道理的。我不太了解“可能性 2”——这是否意味着测试项目之外的变化?
【解决方案2】:

最后我设法把它修好了。实际上可以使用 InlineData - 只是不像使用 nUnit 那样简单(imo)。这是解决方案:

[Theory]
[InlineData("", typeof(OkObjectResult))]
[InlineData(null, typeof(BadRequestObjectResult))]
public async Task Get_Return_Something(param1, param2){

    // moq MyController with its parameters/objects
    ...


    var result = await sut.Get(param1);

    result.ShouldBeOfType(param2);
}

在我看来,这是一种非常简单的方法 - 与在网上搜索时发现的方法相比 - 希望它对其他人也有用。

【讨论】:

    猜你喜欢
    • 2010-09-26
    • 1970-01-01
    • 1970-01-01
    • 2019-08-23
    • 2016-07-06
    • 1970-01-01
    • 1970-01-01
    • 2016-01-29
    相关资源
    最近更新 更多