idea提取代码为方法






这篇文章是“从旧代码到可测试代码”系列的一部分。 在本系列中,我们将讨论在为遗留代码编写测试之前进行重构的步骤,以及它们如何使我们的生活更轻松。

与重命名一样,提取方法有助于我们更好地理解代码。 如果您发现该方法的命名很容易,那就很有意义。 否则,您只需封装执行很多事情的代码。 有时它可能是有用的,尽管不能提取出有意义的小方法。

提取方法还会引入接缝。 现在可以模拟此方法,并且现在可以在测试代码时对其进行影响。 不使用电动工具时的窍门之一是将静态方法与实例方法包装在一起。

在我们的Person类中,我们具有GetZipCode方法:

public class Person { 
    String street; 

    public String getZipCode() { 
        Directory directory = Directory.getInstance(); 
        return directory.getZipCodeFromStreet(street); 
    } 
}

Directory.getInstance()方法是静态的。 如果我们将其提取到getDirectory方法(在Person类中)并使该方法可访问,则现在可以对其进行模拟。

public class Person { 
    String street; 
    
    public String getZipCode() { 
        Directory directory = getDirectory(); 
        return directory.getZipCodeFromStreet(street); 
    }
    
    protected Directory getDirectory() { 
        return Directory.getInstance(); 
    } 
}

尽管现在使用Mockito模拟getDirectory方法非常容易,但是如果我们使用PowerMockito则模拟Directory.getInstance也很容易。 是否有其他理由引入新方法?

如果仅出于测试目的,则无需进行提取。 有时,用电动工具嘲弄事物并不容易。 静态构造函数中出现的问题可能需要在测试侧进行更多处理。 使用单独的方法包装可能会更容易。

有时,无论使用哪种模拟工具,提取都可以帮助我们。 我们甚至可以在编写方法之前使用方法提取来简化测试。 模拟一个方法(而不是3个调用)更加简单安全。

如果我们的getZipCode方法看起来像这样:

public String getZipCode() { 
    Address address = new Address(); 
    address.setStreet(street); 
    address.setCountry(country); 
    address.setState(state); 
    address.setCity(city); 
    
    Directory directory = Directory.getInstance(address); 
    return directory.GetZipCode(); 
}

即使使用了电动工具,伪造Address实例并设置其他行为设置(仅用于检索目录)也需要大量工作,这意味着需要花很长的时间进行较长的测试。 如果我们提取getDirectoryFromAddress方法:

public String getZipCode() { 
    Directory directory = getDirectoryFromAddress(); 
    return directory.GetZipCode(); 
}

我们获得了更具可读性的代码,并且只需要模拟一行。

虽然提取有好处,但行李也要有接缝的方法。 如果该方法是私有的,并且我们使用强大的工具对其进行了模拟,则测试和代码之间的耦合会增加。 如果我们将其公开,则可以有人叫它。 如果受保护,则派生类可以调用它。 可测试性的改变是设计的改变,无论是好是坏。

idea提取代码为方法