【问题标题】:unit testing classes that use concrete classes declared in code body使用代码主体中声明的具体类的单元测试类
【发布时间】:2011-09-05 23:19:33
【问题描述】:

很简单,我想知道的是,有没有办法对这段代码进行干净的单元测试?我可以实例化它并运行一些断言,但我的意思是实际的单元测试,我将在其中模拟服务对象以删除被测类的任何依赖关系,并实际上让它只测试这个类而不是它的依赖服务(在本例中为 ConcreteService)。

public class Foo{

    public SomeResult DoSomething(){
        var service = new ConcreteService();
        var foo = service.Execute();
        return foo;
    }

}

我的正常方法是在我的代码主体中不创建这种类型的对象,但是在无法更改的情况下,我有哪些单元测试选项?

【问题讨论】:

    标签: c# .net unit-testing dependency-injection


    【解决方案1】:

    鉴于限制(您不能修改/重新编译该代码),恐怕除了“集成测试”之外您无能为力 - 真正的依赖关系,缓慢的测试.

    在方法中实例化一个依赖,而不是接受它作为一个 ctor 或方法参数会使事情变得困难。

    正如 Ethan 所说,有痣(但对我个人而言,它似乎并不正确)。我更喜欢设计更改,而不是帮助我掩盖设计问题的框架。

    【讨论】:

    • +1 回答主要问题,“有没有办法干净地对这段代码进行单元测试?”
    • 我不太担心缓慢的测试......更多的是愚蠢的依赖失败并去寻找错误的地方......但我明白你的意思!
    • @Aaron - 另一种气味。在硬编码的位置寻找预期的资源,没有覆盖/传入的选项。我希望我能告诉你有一条出路......对不起
    • -1 您可以使用合适的模拟工具轻松对此类代码进行单元测试,例如 TypeMock Isolator(我同意 Moles,它丑陋的)。此外,我不会说在另一个内部实例化一个具体的服务类必然是一个设计问题。根据我的经验,它通常是最好的选择。
    • @Rogerio - 现在接受依赖项作为 ctor args 是一种常见的做法。尤其是当依赖项对可测试性造成问题时。 TypeMock Isolator 是一个付费工具(如果我没记错的话),所以如果 OP 对此开放,它可能会起作用 - 恕我直言,我会添加一个参数化的 ctor 而不是为专有工具付费。
    【解决方案2】:

    如果 service.Execute() 是虚拟的,那么大多数模拟框架都支持这一点。我个人使用Rhino.Mocks

    请参阅this question,其中详细介绍了在非接口上使用 Mocking 框架的功能,并讨论了 Mocking 框架拦截虚拟调用的功能。

    如果它不是虚拟的,并且您无法更改它,那么您可以使用Moles。上面提到的帖子还提到了使用TypeMock的能力,我个人没有用过。

    微软研究网站上对 Moles 的描述:

    Moles 是一个轻量级的框架,用于 .NET 中基于委托的测试存根和迂回。 Moles 可用于绕过任何 .NET 方法,包括密封类型中的非虚拟/静态方法。

    【讨论】:

    • 在这种情况下你将如何模拟具体服务?请记住,具体类是在被测类中实例化的。我的理解是模拟框架无法处理这种情况,但我可能是错的......
    • 我不明白您将如何通过使执行虚拟化来模拟具体类。我知道你必须在模拟具体类时使成员成为虚拟,因为我以前用 rhinomocks 必须这样做,但我不清楚当它在代码中实例化时,你将如何为被测类提供模拟的具体类。感谢您的输入!
    • @AaronHS - 模拟框架可以拦截虚拟调用。在这种情况下,您仍在实例化ConcreteService,但对Execute 的调用被拦截。
    【解决方案3】:

    我知道您提到这将是您无法更改的情况的一个示例,但我会努力重构该类,以便可以将 ConcreteService 作为依赖项注入。重构是尝试解决糟糕设计的一种更简单的方法。从您发布的内容来看,我认为您没有理由不能这样做。

    编辑: FWIW,我同意@Gishu。如果你绝对不能修改类,例如你不拥有它并且你没有能力修改它,那么集成测试是最好的方法。然而,如果你能看到代码和它在做什么,我支持我之前所说的,你真的没有理由不重构。

    【讨论】:

    • OP 声明代码无法更改,即使它作为依赖项注入,除非您注入接口,否则仍需要专门的框架来 Mock。
    • @Ethan Semantics。您不能注入接口。您注入一个实现接口的对象。您还可以注入一个扩展具体类的存根。杂乱无章,但这是许多方法之一。
    • 因此,如果我将方法 execute() 设为虚拟,它将起作用,但我将模拟被测类而不是模拟类依赖项......?我通常认为你模拟依赖项并使用常规实例化(传入模拟)来测试类......或者这只是测试这些糟糕场景的工作?
    • @j0k,具体类存根仍然必须将 Execute 方法标记为虚拟,大多数 Mocking 框架才能工作。是的,我应该说“作为接口的实例注入”。而且,由于不能在带有“.net”标签的帖子中假设这一点,我将明确提到接口是指四人组版本(即抽象类、具有虚拟方法的基类、实际接口、.. .)
    • @AaronHS - 你熟悉依赖注入吗?如果没有,我可以通过一个示例来更新我的答案,说明如何重构您发布的代码以使用它。
    猜你喜欢
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 2019-03-24
    • 2015-03-16
    • 1970-01-01
    相关资源
    最近更新 更多