The value of high level unit tests and mock objects
中讨论了类似的主题但是,我想描述一下具体情况,并询问您对如何编写单元测试的看法。
我正在开发一个使用Entity Framework的普通3层应用程序。在EF之上,我有两层:
如果没有进一步的描述,这里是我要编写单元测试的类:
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
,可以轻松地对PersonManager
和PersonRepository
进行单元测试。
但我找不到任何方便的方法来进行单元测试ComplexMethod
。
您对我该怎么做有什么建议吗?或者根本不应该进行单元测试?也许我不应该将this
引用用于ComplexMethod
中的方法调用,而是通过接口访问PersonManager
本身,并将其替换为模拟呢?
提前感谢任何建议。
答案 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进行单元测试。
这有意义吗?