单元测试一个高抽象级别的方法

时间:2011-10-02 10:06:57

标签: c# unit-testing mocking

The value of high level unit tests and mock objects

中讨论了类似的主题

但是,我想描述一下具体情况,并询问您对如何编写单元测试的看法。

我正在开发一个使用Entity Framework的普通3层应用程序。在EF之上,我有两层:

  • 存储库:他们直接访问EF ObjectContext并执行所有CRUD工作(实际上,这些类是使用T4模板生成的)。所有Repository类都有一个合适的接口。
  • 管理员:他们实现更高级别的业务逻辑,他们不直接访问ObjectContext,而是使用适当的存储库。管理人员不知道具体的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.
    }
}

通过模拟SimpleMethod1实例化SimpleMethod2,可以轻松地对PersonManagerPersonRepository进行单元测试。

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

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

提前感谢任何建议。

2 个答案:

答案 0 :(得分:6)

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

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

不久之后,他们通常会发现部分嘲笑。使用部分模拟,您可以模拟SimpleMethod1()和SimpleMethod2(),然后使用常规模拟机制测试ComplexMethod()。 “听起来不错,”他们认为,“这将彻底解决我的问题!”。但是,一个好的模拟框架应该强烈反对以这种方式使用部分模拟,因为现实是:

您的测试告诉您有关设计问题的信息。

具体来说,他们告诉你,你在一个课程中混合了关注点和/或抽象级别。他们告诉你SimpleMethod1()和SimpleMethod2()应该被提取到这个类所依赖的另一个类。无论我看到多少次这种情况,无论开发人员多么激烈地争论,最终都会在100%的时间内证明这些测试是正确的。

答案 1 :(得分:4)

我看不出问题所在。您可以在模拟存储库时测试复杂的方法,没有问题。

你需要两个单元测试,每个单元测试都会使用与SimpleMethod1测试中相同的期望和执行顺序(我假设你已经有两个单元测试用于SimpleMethod1,一个用于返回“ true“,一个用于”false“)以及您对测试SimpleMethod2的期望值分别为固定参数1或2。

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

另请注意,您对SimpleMethod2的测试不应对传递的参数做出任何假设:在“真实”中,您只能使用1或2作为参数(这就是您对ComplexMethod的单元测试所具有的),但是你对SImpleMethod2的单元测试应该测试它的参数是什么:any int。

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

这有意义吗?