【问题标题】:Unit test with Dependency Injection using struture map使用结构映射进行依赖注入的单元测试
【发布时间】:2014-08-11 10:19:41
【问题描述】:

我是 MVC 和 IoC 以及其他相关概念的新手。

我必须为一个旧项目编写单元测试用例。它使用结构图进行依赖注入。我使用的第三方 dll 暴露了一些接口。现在我必须为控制器类中的方法编写单元测试用例。

控制器的构造函数如下所示:

HomeController(IClientData clientdata)
{
    _clientdata = clientdata;
}
//The clientdata was initialized

在我的测试用例中,如何初始化控制器类和接口,如IClientData?将IClientData clientdata 传递给构造函数表示:passing type as var

如何初始化/模拟IClientData 中的数据?不知道dll中接口是怎么实现的。

【问题讨论】:

    标签: c# asp.net-mvc unit-testing


    【解决方案1】:

    为了在单元测试中使用接口的实现,您应该使用像Moq 这样的模拟框架,并且您可以设置这些接口以返回满足您的测试要求的默认值。在这种情况下,您将创建一个 Mock 来设置您希望在此特定预期用例中看到的值,然后将其传递给您的控制器。

    var mockClientData = new Mock<IClientData>();
    
    mockClientData.SetupGet(data => data.MyProperty).Returns(3);
    // mockClientData.Object.MyProperty now returns 3
    
    mockClientData.Setup(data => data.MyMethod()).Returns(42);
    // mockClientData.Object.MyMethod() now returns 42
    // any other setup that you need done goes here
    
    var controller = new HomeController(mockClientData.Object);
    
    // the rest of your test as normal
    

    请务必注意,您不应在单元测试中依赖依赖项的实现,因为这样您就不再是在测试代码单元 - 您正在编写集成测试。

    【讨论】:

    • 如果我使用moq,我必须自己设置数据,对吗?但问题是:我的控制器从一组通过构造函数中传递的 IClientdata 初始化的客户端数据开始。我想使用相同的初始数据。不想自己设置。我该怎么做?
    • 我当然会模拟传递给我需要测试的相关函数的数据。
    • 我在这里有点困惑。 My controller starts with a set of clientdata that is initialized via IClientdata passed in constructor. 您应该将模拟传递给构造函数,该构造函数设置为返回您希望看到的数据。然后使用该数据代替您将在生产期间传入的IClientData
    • 如果你在做一个单元测试,你真的应该自己设置所有的数据,这样你就可以删除任何外部依赖或“假设”。单元测试应该是一个黑盒,在您可以完全控制的受控环境中执行 - 这包括设置任何变量并控制它们。
    • 好的。我会尝试再次解释。有一组通过 IClientdata 传递给构造函数的默认数据。我猜这是由di(结构图here)完成的。我想使用相同的数据来初始化构造函数,然后通过根据需要模拟数据来对我的函数进行单元测试。
    【解决方案2】:

    不要试图在单元测试中模仿现实世界。如果给定一组先决条件,单元测试应验证某个单元(此处为控制器)是否以指定的方式运行。

    根据 Dan 的回答,我了解到您似乎并不确切知道控制器在现实生活中获得了哪些数据。如果您确切地知道先决条件必须是什么样子以及控制器在这些条件下应该如何表现,则只能编写单元测试。所以你有两个选择:

    • 尝试通过检查构建IClientData 所涉及的任何进程来找出真实数据。这需要您检查 dll 的源代码。

    • 分析控制器的源代码。它在哪里访问IClientData?它执行哪些检查?基于此,您可以得出所需的结论以及输入如何影响控制器的行为。然后,您可以编写一个记录该内容的单元测试。

    第二个选项意味着您创建记录“是”状态的测试,即使该状态理论上可能偏离最初的预期。您可能没有其他选择,因为您回想起来编写测试。但至少单元测试的名称和内容会使那些隐含的东西非常明确。您可以将您的测试交给开发人员并询问他们:“这就是控制器的行为方式。正确吗?”

    无论您选择什么选项,您最终都必须了解先决条件和预期行为是什么。一旦你有了它,你就可以通过使用Moq 或任何其他方式来写下这个,就像 Dan 的回答那样写存根或模拟。对于每个测试用例,您编写一个或多个测试方法。每种方法只做出一个逻辑断言。因此,每个测试方法也应该只需要对您的 IClientData 模拟进行非常有限的一组初始化。

    tl;博士?

    【讨论】:

    • 感谢您的冗长解释。我很感激。问题是,我可以模拟接口并使用它来初始化控制器,但接口很大,模拟它需要很多努力。例如:有多个数据成员是类。我不可能嘲笑一切。所以我想使用默认实现,并且只模拟我需要测试的函数的参数。
    • 巨大界面中的每个字段都是您当前正在编写的测试所必需的
    • 是的。一个是必要的,其余的都依赖于它。这是一个复杂而庞大的项目。
    • 对不起,三重帖子 - 可能值得您向我们展示一个需要测试的方法,以便我们指出您将如何模拟 IClientData,以便您了解它是如何工作的
    • @Maxsteel:你可能对什么是单元测试有一些误解。一种单元测试方法应该只测试一个特定的小东西,例如控制器上的一种方法调用。 Moq 将为整个接口创建一个默认实现。只有行为与被测方法相关的部分才需要显式模拟。如果控制器中的单个方法在您的界面上使用了很多调用,这可能是您告诉您的雇主遗留代码不可单元测试的情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多