它是。
即使只进行单元测试,测试中包含的代码也要比实际测试的代码多。没有什么问题。
考虑一个简单的代码:
public void SayHello(string personName)
{
if (personName == null) throw new NullArgumentException("personName");
Console.WriteLine("Hello, {0}!", personName);
}
将会进行哪些测试?这里至少要测试四个简单的情况:
人名是null
。实际抛出异常了吗?至少要编写三行测试代码。
人名是"Jeff"
。我们有"Hello, Jeff!"
回应吗?那是四行测试代码。
人名是一个空字符串。我们期望什么输出?实际输出是多少?附带问题:是否符合功能要求?这意味着用于单元测试的另外四行代码。
人名对于一个字符串来说足够短,但是太长而不能与"Hello, "
感叹号结合使用。会发生什么?¹
这需要大量的测试代码。此外,最基本的代码片段通常需要设置代码,该代码初始化被测代码所需的对象,这通常还导致编写存根和模拟等。
如果比率很大,在这种情况下,您可以检查以下几件事:
测试之间是否存在代码重复?它是测试代码这一事实并不意味着该代码应在相似的测试之间重复(复制粘贴):这样的重复将使这些测试的维护变得困难。
有多余的测试吗?根据经验,如果删除单元测试,则分支覆盖率应降低。如果不是,则可能表明不需要测试,因为其他测试已经覆盖了路径。
您是否仅测试应测试的代码?您不应该测试第三方库的基础框架,而只能测试项目本身的代码。
使用冒烟测试,系统和集成测试,功能和验收测试以及压力和负载测试,您甚至还要添加更多的测试代码,因此您不必担心为每个实际代码的LOC拥有四个或五个LOC测试。
关于TDD的注意事项
如果您担心测试代码所花费的时间,则可能是您做错了,即先进行代码,再进行测试。在这种情况下,TDD可能会通过鼓励您在15-45秒的迭代中进行工作(在代码和测试之间进行切换)来提供帮助。根据TDD的支持者,它通过减少您需要执行的测试数量,更重要的是,减少了为测试编写和编写的业务代码的数量,从而加快了开发过程。
¹令n为一串的最大长度。我们可以调用SayHello
并通过引用传递长度为n -1 的字符串,该字符串应该可以正常工作。现在,在Console.WriteLine
步骤中,格式化应以长度为n + 8 的字符串结尾,这将导致异常。可能由于内存限制,即使是包含n / 2个字符的字符串也会导致异常。一个人应该问的问题是,第四项测试是否是单元测试(看起来像单元测试,但与平均单元测试相比,它在资源方面的影响可能更大),以及它是否测试实际的代码或基础框架。