Unit testing 如何在编写源代码之前编写单元测试?
由于单元测试是白盒测试,它假定您必须提前知道代码必须处理的所有情况、代码必须处理的所有客户端对象(在测试中称为模拟对象)以及客户端对象必须在代码中出现的正确顺序(因为单元测试考虑了模拟对象的调用)。换句话说,您必须确切地知道代码的详细算法。在你能确切地知道你的代码的算法之前,你必须先写它 从我的观点来看,我不认为在编写源代码之前编写正确的单元测试是可能的。尽管如此,还是可以先编写功能测试,因为功能测试是一种用户需求。 你的建议? 致意 这个问题给出了一个例子:Unit testing 如何在编写源代码之前编写单元测试?,unit-testing,tdd,Unit Testing,Tdd,由于单元测试是白盒测试,它假定您必须提前知道代码必须处理的所有情况、代码必须处理的所有客户端对象(在测试中称为模拟对象)以及客户端对象必须在代码中出现的正确顺序(因为单元测试考虑了模拟对象的调用)。换句话说,您必须确切地知道代码的详细算法。在你能确切地知道你的代码的算法之前,你必须先写它 从我的观点来看,我不认为在编写源代码之前编写正确的单元测试是可能的。尽管如此,还是可以先编写功能测试,因为功能测试是一种用户需求。 你的建议? 致意 这个问题给出了一个例子: 显然,如果您不了解“软件”的意图,
显然,如果您不了解“软件”的意图,您就无法编写测试,但如果要求或规范详细说明,则绝对有可能编写测试 你可以写一些符合要求的东西,但会失败;然后根据规范进行最低工作量的测试,使测试通过 除非你是个天才,否则这第一步需要重构和引入抽象,模式、可维护性、性能和所有其他方面都需要考虑 因此,如果理解了需求,您可以先进行测试,但是测试将不会通过,直到实现结合在一起,并且只需要使测试通过的实现
以这种方式工作并不总是符合实际情况,特别是在很难获得规范的情况下。如果你没有得到作为开发人员所需要的东西,你需要小心,不要盲目地沿着这条路走下去。在继承代码或向“棕色地带”项目添加代码时,通常也无法实现这一点。作为一名开发人员,很重要的一点是要尽早确定实用性。这对于启动TDD的人来说是一个很难克服的问题。我相信在SO上有很多很好的答案,尤其是在programmers.stackexchange.com上(这个问题可能更适合那个论坛)
我可以说很多东西来帮助你理解,但是没有一件能像你实际做TDD那样有效。作为一个快速的开始,我将向您介绍一些有用的TDD练习的链接 不太好。有了TDD,您就可以从预期产品代码将要处理的测试用例开始。这并不意味着不应该有产品代码。您的类、函数等可以存在。您从失败的测试用例开始,然后继续更改产品代码以使其通过 看 换句话说,您必须确切了解代码的详细算法 不完全是。您必须确切地知道代码的详细行为,正如从代码本身外部观察到的那样。实现此行为的算法、算法组合或任何抽象/嵌套/计算级别等对测试都不重要。测试只关心达到预期结果 因此,测试的价值在于它们是代码应该如何运行的规范。因此,只要代码仍然可以根据测试进行验证,代码就可以随意更改。您可以改进性能、重构可读性和可支持性等。测试确保行为保持不变 例如,假设我想写一个函数,它将两个数字相加。你可能在脑子里知道你将如何实现它,但暂时把这些知识放在一边。你还没有实现它。首先,您正在实施测试
public void CanAddIntegers()
{
var addend = 1;
var augend = 1;
var result = MyMathObject.Add(addend, augend);
Assert.AreEqual(2, result);
}
现在您已经有了一个测试,您可以实现该方法
public int Add(int addend, int augend)
{
return ((addend * 2) + (augend * 2)) / 2;
}
哇。等一下。。。我究竟为什么要这样实施呢?从测试的角度看,谁在乎呢?它过去了。实现符合要求。现在我有了一个测试,我可以安全地重构代码
public int Add(int addend, int augend)
{
return addend + augend;
}
public int Add(int addend, int augend)
{
return 2;
}
这有点理智。而且测试还是通过了。事实上,我可以进一步减少代码
public int Add(int addend, int augend)
{
return addend + augend;
}
public int Add(int addend, int augend)
{
return 2;
}
你猜怎么着?考试还是通过了。这是我们唯一的测试,它是唯一给出的规范,因此代码“有效”。因此,显然我们需要改进测试以涵盖更多的情况。编写更多的测试将为我们提供编写更多代码所需的规范
事实上,最后一次实施应该是第一次,根据:
不允许您编写的生产代码超过足以通过一个失败的单元测试的数量
因此,在一个纯粹由Bob叔叔驱动的TDD世界中,我们应该先编写最后一个实现,然后编写更多测试并逐步改进代码
这就是所谓的红、绿、重构循环。这在一个简单的稍微不那么做作的例子中得到了很好的说明。该练习的目的是练习该循环:
public class MyMathObject
{
public MyMathObject(MyOtherClass1 firstDependency, MyOtherClass2 secondDependency)
{ ^---Right here ^---And here
// implementation details
}
public int Add(int addend, int augend)
{
// implementation details
}
}
private SomeInternalFunction()
{
var firstDependency = ServiceLocatorObject.Resolve<MyOtherClass1>();
// implementation details
}
public class MyMathObject
{
public int Add(int addend, int augend)
{
// implementation details
}
}