代码之家  ›  专栏  ›  技术社区  ›  MSI

静态方法:何时和何时不

  •  8
  • MSI  · 技术社区  · 15 年前

    我是TDD和DDD的新手,我有一个关于静态方法的简单问题。TDD的大多数专家用一句话来说,静态方法是不好的(我们应该忘记创建大量的静态实用程序,因为它们是不可测试的,我们(um或I)以前曾经使用过它们。我明白为什么它们是不可测试的(可以找到一篇很好的澄清文章 here 对于那些有兴趣,但我想我是这里唯一的人:)但我想知道是否有一个好的和干净的指导方针,使用静态从TDD的角度来看?

    对你们大多数人来说,这个问题可能真的很傻,但有些建议会很好,我只想知道这里的专家是如何看待静态的东西的。提前谢谢。

    编辑:在寻找答案的过程中,我发现了另外两个关于使用static(虽然不涉及TDD)的好线程,我想这对那些感兴趣的人(包括我自己)来说是很好的阅读。

    3 回复  |  直到 9 年前
        1
  •  11
  •   Carl Manaster    15 年前

    我想你可能有点误解了。

    public static int Add(int x, int y)
    {
        return x + y;
    }
    

    您可以通过测试返回值是否为您根据传入的参数所期望的值来对此进行测试。

    当测试需要引入模拟时,静态方法会变得麻烦。

    假设我有一些代码调用 File.Delete() 静态方法。为了在不依赖文件系统的情况下测试我的代码,我想用一个测试版本来替换/模拟这个调用,该版本只验证它是否已经从正在测试的代码中调用。如果我有一个对象的实例 Delete()

    为了测试这样的东西,我将引入一个接口:

    interface IFileDeleter
    {
        void Delete(string file);
    }
    

    然后,我的代码将获取实现此接口的对象的实例(在方法调用中或作为构造函数中的参数),然后调用 删除方法:

    void MyMethod(string file)
    {
        // do whatever...
        deleter.Delete(file);
    }
    

    为了测试这个,我可以嘲笑 IFileDeleter 接口并简单地验证 删除() 方法已被调用。这消除了在测试过程中需要真正的文件系统的需要。

    这可能看起来代码更复杂(它是这样的),但它会让测试变得更容易。

        2
  •  8
  •   Chris Knight    15 年前

    避免静态当然是一种方法,但是当您不能或者正在使用遗留代码时,可以使用以下选项。下面是阿德里安班克斯对上述问题的回答,假设您有以下代码(抱歉,它是用Java编写的,因为我不知道C#):

    public void someMethod() {
       //do somethings
       File.delete();
       //do some more things
    }
    

    public void someMethod() {
       //do somethings
       deleteFile();
       //do some more things
    }
    
    //protected allows you to override in a subclass
    protected void deleteFile() { 
       File.delete();
    }
    

    然后在准备单元测试时,创建一个模拟类,该类扩展了原始类并终止了该功能:

    //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()的所有功能,而没有实际删除该文件,您仍然可以查看是否调用了该方法。

        3
  •  7
  •   TrueWill    15 年前

    一般来说,如果方法:

    • 很长
    • 包含复杂逻辑
    • 使用文件系统
    • 调用web服务

    那就不要让它静止。(请参阅@adrianbanks的答案,以获得有关此问题背后的原因和备选方案的精彩讨论。)

    推荐文章