我是TDD和DDD的新手,我有一个关于静态方法的简单问题。 TDD的大多数专家用一句话说静态方法是坏的(我们应该忘记创建大量的静态实用程序,我们(或我)之前用过它们是不可测试的。我可以看出为什么它们是不可测试(对于那些感兴趣的人我可以找到一个很好的澄清文章here,但我想我是这里唯一的noob :()但我想知道是否有一个很好的和干净的指南使用TDD点的静态查看?
对于你们大多数人来说,这可能是一个非常愚蠢的问题,但是一些提示会很棒,我只是想知道这里的专家如何看待静态的东西。提前致谢。
编辑:在寻找答案的过程中,我发现了另外两个关于静态使用的好线程(尽管不是TDD问题),对于那些感兴趣的人(我自己也包括在内),我认为这些线程很好。
答案 0 :(得分:11)
我想你可能会有些误解。
静态方法是可测试的。以此方法为例:
public static int Add(int x, int y)
{
return x + y;
}
您可以通过根据传入的参数测试返回值是否符合预期来测试这一点。
当测试是需要引入模拟时,静态方法会变得很麻烦。
假设我有一些调用File.Delete()
静态方法的代码。为了在不依赖文件系统的情况下测试我的代码,我想用一个测试版本替换/模拟这个调用,该测试版本只是验证它是从被测试的代码调用的。如果我有一个正在调用Delete()
的对象的实例,这很容易做到。大多数(所有?)模拟框架都不能模拟静态方法,所以在我的代码中使用静态方法迫使我以不同方式测试它(通常通过调用真正的静态方法)。
为了测试这样的东西,我会介绍一个接口:
interface IFileDeleter
{
void Delete(string file);
}
然后我的代码将获取实现此接口的对象的实例(在方法调用中或作为构造函数中的参数),然后调用其Delete()
方法来执行删除:
void MyMethod(string file)
{
// do whatever...
deleter.Delete(file);
}
为了测试这个,我可以模拟IFileDeleter
接口并简单地验证它的Delete()
方法已被调用。这样就不需要将真实的文件系统作为测试的一部分。
这可能看起来代码更复杂(实际上是这样),但它使自己更容易测试。
答案 1 :(得分:8)
避免静态当然是要走的路,但是当你不能或者你正在使用传统代码时,可以使用以下选项。继上面的adrianbanks回答之后,假设您有以下代码(道歉,它在Java中,因为我不知道C#):
public void someMethod() {
//do somethings
File.delete();
//do some more things
}
您可以将File.delete()重构为自己的方法,如下所示:
public void someMethod() {
//do somethings
deleteFile();
//do some more things
}
//protected allows you to override in a subclass
protected void deleteFile() {
File.delete();
}
然后在准备你的单元测试时创建一个mock类,它扩展了原来的类并且存根该功能:
//Keep all the original functionality, but stub out the file delete functionality to
//prevent it from using the real thing and while you're at it, keep a record that the
//method was called.
public class MockClass extends TheRealClass {
boolean fileDeleteCalled = false;
@Override
protected void deleteFile()
//don't actually delete the file,
//just record that the method to do so was called
fileDeleteCalled = true;
}
public boolean fileDeleteCalled() {
return fileDeleteCalled;
}
}
最后在你的单元测试中:
//This would normally be instantiated in the @Before method
private MockClass unitUnderTest = new MockClass();
@Test
public void testFileGetsDeleted(){
assertFalse(unitUnderTest.fileDeleteCalled());
unitUnderTest.someMethod();
assertTrue(unitUnderTest.fileDeleteCalled());
}
现在你已经执行了someMethod()的所有功能而没有实际删除文件,你仍然能够看到是否调用了该方法。
答案 2 :(得分:7)
一般来说,如果方法:
然后避免使其静止。 (请参阅@adrianbanks的回答,以便对此背后的原因和替代方案进行深入讨论。)
基本上,如果它是一个简短的内存便利方法(就像许多扩展方法一样),只能使它成为静态。