2011-10-02 46 views
3

类似的话题已经在The value of high level unit tests and mock objects单元测试是在高的抽象层次

但是讨论的方法,我想描述一个具体情况,问你对我应该怎么写单元测试的意见。

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

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

没有进一步的说明,这里是我想编写单元测试的类:

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. 
    } 
} 

它是用的一个模拟实例化PersonManager很容易单元测试SimpleMethod1SimpleMethod2PersonRepository

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

你有什么建议我应该怎么做?或者不应该单元测试?也许我不应该使用this参考ComplexMethod中的方法调用,而是通过一个接口访问PersonManager本身,并用一个模拟代替它。

在此先感谢您的任何建议。

+1

为什么你不能测试ComplexMethod?你可能不需要为SimpleMethod 1&2测试设置额外的期望... –

回答

5

纪尧姆的回答很好(+1),但我想提供一个额外的观察。我在你发布的代码中看到的是一个很普遍的问题的基础,这是人们试图找出(或反驳)TDD的一个常见问题,它是:

“我/我为什么要测试ComplexMethod(),因为它取决于SimpleMethod1()和SimpleMethod2(),它们已经过测试,并且具有它们自己的行为,我必须在ComplexMethod()的测试中考虑它们吗?我必须基本上复制SimpleMethod1()和SimpleMethod2 ()为了完全测试ComplexMethod(),这只是愚蠢的。“

不久之后,他们通常会发现部分嘲讽。使用部分模拟,你可以模拟SimpleMethod1()和SimpleMethod2(),然后使用普通模拟机制测试ComplexMethod()。 “听起来很棒,”他们认为,“这将完美解决我的问题!”。但是,一个好的模拟框架应该强烈地阻止以这种方式使用部分模拟,因为现实是:

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

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

+0

+1听测试! – TrueWill

+1

完全同意!我甚至会补充说,如果他开始编写测试(TDD或BDD,如果使用模拟),他很可能不会因为这个设计问题而结束,但是这两个类会从测试中出现“有机” – Guillaume

+0

谢谢对于这个答案,这很有道理。然而,在此之前,我没有发现在同一个类中具有不同抽象层次上的方法的设计问题。我的意思是,在我的应用程序中为每个抽象级别创建一个新类时感觉有点模糊。在上面的例子中,我处于一种特定的情况,因为我加入了一个长期运行的项目,之前没有任何单元测试,我应该刚开始编写测试......但是在下一个“新鲜”项目我一定会尝试遵循你的建议,谢谢! –

3

我没有看到问题所在。您可以在嘲笑存储库时测试复杂的方法,这没有问题。你会需要两个单元测试,每个测试都会使用与SimpleMethod1测试中相同的期望和执行顺序(我假设你已经有了两个针对SimpleMethod1的单元测试,一个用于返回“true”,一个用于“false”),以及您对SimpleMethod2测试的相同期望,分别使用固定参数1或2。

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

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

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

这有道理吗?

+0

感谢您的答案。事实上,这不是真正的生活的例子,只是我画的一个简单的例子。在实际的应用程序中,ComplexMethod要复杂得多,并且调用10个或更多更简单的类的方法,并且可以有两个以上的可能执行路径。因此,测试所有执行路径并测试更简单方法的功能几乎是不可能的。我想我将尝试将'this'引用改为一些静态调用,我可以在测试中用模拟替换。 –