【发布时间】:2010-11-18 23:45:20
【问题描述】:
我一直在阅读,使用 TDD 时往往会避免使用静态方法,因为它们往往难以模拟。但我发现,最容易进行单元测试的是具有简单功能的静态方法。不必实例化任何类,鼓励方法简单、只做一件事、“独立”等。
有人能解释一下 TDD 最佳实践和实用性之间的差异吗?
谢谢, 一个
【问题讨论】:
标签: c++ unit-testing
我一直在阅读,使用 TDD 时往往会避免使用静态方法,因为它们往往难以模拟。但我发现,最容易进行单元测试的是具有简单功能的静态方法。不必实例化任何类,鼓励方法简单、只做一件事、“独立”等。
有人能解释一下 TDD 最佳实践和实用性之间的差异吗?
谢谢, 一个
【问题讨论】:
标签: c++ unit-testing
静态方法很容易测试,但是直接调用静态方法的东西一般不依赖于它所依赖的静态方法来测试是不容易的。使用非静态方法,您可以使用 stub/mock/fake 实例来简化测试,但如果您正在测试的代码调用静态方法,它实际上是“硬连线”到该静态方法。
【讨论】:
在我看来,对所提问题的回答是“面向对象似乎是人们思考 TDD 的全部内容。”
为什么?我不知道。也许他们都是 Java 程序员,感染了让一切都依赖于六个间接层、依赖注入和接口适配器的疾病。
Java 程序员似乎喜欢预先把所有事情都弄得很困难,以便“以后节省时间”。
我建议在您的 TDD 中应用一些敏捷原则:如果它没有导致问题,则不要修复它。不要过度设计。
在实践中,我发现如果静态方法首先经过良好测试,那么它们不会成为调用者出现错误的原因。
如果静态方法执行速度很快,那么它们就不需要模拟。
如果静态方法与程序外部的东西一起工作,那么您可能需要一个模拟方法。在这种情况下,您需要能够模拟许多不同类型的函数行为。
如果您确实需要模拟静态方法,请记住在 OO 编程之外可以使用它。
例如,您可以编写脚本来将源代码处理为调用模拟函数的测试表单。您可以将具有不同版本功能的不同目标文件链接到测试程序中。您可以使用链接器技巧来覆盖函数定义(如果它没有被内联)。我敢肯定还有一些我没有在这里列出的技巧。
【讨论】:
测试静态方法很容易。问题是在测试其他代码时,无法将其他代码与该静态方法隔离开来。调用代码与静态代码紧密耦合。
对静态方法的引用不能被许多模拟框架模拟,也不能被覆盖。
如果您有一个进行大量静态调用的类,那么要对其进行测试,您必须为所有这些静态调用配置应用程序的全局状态 - 因此维护成为一场噩梦。如果你的测试失败了,那么你就不知道是哪一段代码导致了失败。
弄错了,是许多开发人员认为 TDD 是无稽之谈的原因之一。他们为测试结果投入了巨大的维护工作,这些结果只能模糊地表明出了什么问题。如果他们只是减少代码单元之间的耦合,那么维护将是微不足道的,并且测试结果是特定的。
【讨论】:
这个建议在很大程度上是正确的.. 但并非总是如此。我的 cmets 不是 C++ 特定的..
举个例子
public class MyStaticClass
{
static int __count = 0;
public static int GetAddCount()
{ return ++__count; }
public static int Add(int operand1, int operand2)
{ return operand1 + operand2; }
// needed for testability
internal static void ResetCount()
{
__count = 0;
}
}
...
//test1
MyStaticClass.Add(2,3); // => 5
MyStaticClass.GetAddCount(); // => 1
// test2
MyStaticClass.Add(2,3); // => 5
//MyStaticClass.ResetCount(); // needed for tests
MyStaticClass.GetAddCount(); // => unless Reset is done, it can differ from 1
【讨论】: