单元测试是一种高度抽象的方法

本文关键字:一种 高度 抽象的 方法 单元测试 | 更新日期: 2023-09-27 18:10:18

在高级单元测试和模拟对象的价值

中讨论了类似的主题。

然而,我想描述一个具体的情况,并询问您关于我应该如何编写单元测试的意见。

我正在开发一个普通的三层应用程序,它使用实体框架。在EF上面,我有两个图层:

  • Repositories:它们直接访问EF ObjectContext并完成所有的CRUD工作(实际上,这些类是用T4模板生成的)。所有Repository类都有一个合适的接口。
  • manager :它们实现更高级的业务逻辑,它们不直接访问ObjectContext,而是使用适当的Repository。管理人员不知道具体的repository实现,只知道接口(我在单元测试中使用依赖注入和模拟)。

不作进一步描述,下面是我想为其编写单元测试的类:

public class PersonManager
{
    private IPersonRepository personRepository; // This is injected.
    // Constructor for injection is here.
    public void ComplexMethod()
    {
        // High level business logic
        bool result = this.SimpleMethod1();
        if(result)
            this.SimpleMethod2(1);
        else
            this.SimpleMethod2(2);
    }
    public bool SimpleMethod1()
    {
        // Doing some low-level work with the repository.
    }
    public void SimpleMethod2(int param)
    {
        // Doing some low-level work with the repository.
    }
}

通过用PersonRepository的模拟实例化PersonManager,可以很容易地对SimpleMethod1SimpleMethod2进行单元测试。

但是我找不到任何方便的方法来单元测试ComplexMethod

你有什么建议我应该怎么做吗?或者根本不应该进行单元测试?也许我不应该为ComplexMethod中的方法调用使用this引用,而是通过接口访问PersonManager本身,并将其替换为mock ?

提前感谢您的建议

单元测试是一种高度抽象的方法

纪尧姆的回答很好(+1),但我想给出一个额外的观察。我在你发布的代码中看到的是人们试图弄清楚(或反对)TDD的一个非常常见的问题的基础,这是:

"我如何/为什么要测试ComplexMethod(),因为它依赖于SimpleMethod1()和SimpleMethod2(),这已经测试过了,并且有自己的行为,我必须在ComplexMethod()的测试中考虑?"为了完全测试ComplexMethod(),我基本上必须复制SimpleMethod1()和SimpleMethod2()的所有测试,这是愚蠢的。"

之后,他们通常会发现部分模拟。使用部分模拟,您可以模拟SimpleMethod1()和SimpleMethod2(),然后使用正常的模拟机制测试ComplexMethod()。"听起来不错,"他们想,"这将完美地解决我的问题!"一个好的模拟框架应该强烈反对以这种方式使用部分模拟,因为现实情况是:

你的测试告诉你一个设计问题。

具体地说,它们告诉您,您在一个类中混合了关注点和/或抽象级别。它们告诉你SimpleMethod1()和SimpleMethod2()应该被提取到这个类所依赖的另一个类中。无论我多少次看到这种场景,无论开发人员如何激烈地争论,最终测试都被证明是正确的。

我不明白是什么问题。您可以在模拟存储库的同时测试您的复杂方法,没有问题。

您将需要两个单元测试,每个单元测试将使用与您在SimpleMethod1测试中相同的期望和执行顺序(我假设您已经为SimpleMethod1进行了两个单元测试,一个用于返回"true",一个用于返回"false"),并且您对测试SimpleMethod2具有相同的期望,分别具有固定参数1或2。

当然,在你的测试类中会有一些"重复",但这不是问题。

还请注意,您的SimpleMethod2测试不应该对传递的参数做任何假设:在"现实生活"中,您只能将1或2作为参数(这就是您的ComplexMethod单元测试将具有的),但您的SimpleMethod2单元测试应该测试它,无论参数是什么:任何int。

最后,如果ComplexMethod是调用SimpleMethod1和/或SimpleMethod2的唯一方法,你应该考虑使它们私有,并且只对ComplexMethod进行单元测试。

明白了吗?