【发布时间】:2014-08-04 20:17:17
【问题描述】:
我将从简短的例子开始我的问题:
SomeResult DoSomething(input)
{
var a = svc1.getA(input);
if (condition with a)
{
var b = svc2.getB(a);
if (cond with b)
{
var c = svc3.getC(b);
if (cond with c)
{
}
else
{
}
}
else
{
}
}
else
{
}
}
我相信这里的想法很清楚。我们有复杂的分支逻辑,其中条件取决于注入服务返回的中间结果。
当我们想要cond with c 的部分时,我们必须模拟svc1 和svc2 和svc3。
要出现在cond with b,我们必须模拟svc1 和svc2。
因此,每次我们更深入时,我们都会重播执行路径的所有上部。猜猜它通常是怎么做的?宾果游戏,复制粘贴!
我们有一堆单元测试,其中大部分行都被对象的(a,b,c ...)初始化和服务模拟占用。当a、b 或c 是具有数十个属性的对象时,这一切看起来就像一个真正的地狱。 cond with a 的微小变化可以轻松同时破坏 20 个测试。
我坚持有一些“跳到我想测试的地方”的概念。
如果我们这样修改代码会怎样:
SomeResult DoSomething(input)
{
var a = svc1.getA(input);
if (condition with a)
{
var b = svc2.getB(a);
if (cond with b)
{
ProcessBLikeThis(b);
}
else
{
}
}
else
{
}
}
然后我们可以将ProcessBLikeThis与不相关的逻辑分开测试。
然而,为了使其可测试,它必须是公开的。此外,由于我们希望通过测试验证 ProcessBLikeThis 是根据cond with b 使用给定参数调用的,因此我们需要使用隔离器或使ProcessBLikeThis 成为某个接口的方法。
但是,除了 DRY-adherent 可测试性之外,这种粒度设计没有其他必要性。
因此,我希望能提供一些关于如何设计和测试此类方法的指导。
加法:
我也忘了提到我的队友强烈反对将初始化逻辑放在可重用的方法中,因为他们认为可以放在那里和不能放在那里之间没有严格的界限,并期望有一天有人会扩展代码并破坏测试逻辑。他们更喜欢复制粘贴作为一种隔离手段。
【问题讨论】:
-
你是说初始化逻辑不是一个选项?或者只是你的队友不喜欢它?
-
对我来说,这不仅是一种选择,而且是很自然的事情。是的,他们有一些恐惧而不是不喜欢。
-
你在使用 C# 和 Visual Studio 吗?
-
C#,VS2013: MSTest + TypeMockIsolator
-
您需要以不同的方式分解代码,以使其既可维护又可测试。这里给出的例子让我想起了the example problem that I here explain how to address。
标签: unit-testing testing dry