Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Legacy Code To Testable Code #2: Extract Method

DZone's Guide to

Legacy Code To Testable Code #2: Extract Method

· DevOps Zone
Free Resource

Download the blueprint that can take a company of any maturity level all the way up to enterprise-scale continuous delivery using a combination of Automic Release Automation, Automic’s 20+ years of business automation experience, and the proven tools and practices the company is already leveraging.

This post is part of the “Legacy Code to Testable Code” series. In the series we’ll talk about making refactoring steps before writing tests for legacy code, and how they make our life easier.

As with renaming, extracting a method helps us understand the code better. If you find it easy to name the method, it makes sense. Otherwise, you just enclosed code that does a lot of things. It can be useful sometimes, although not as extracting small methods that make sense.

Extracting a method also introduces a seam. This method can now be mocked, and can now affect the code as it being tested. One of the tricks when not using power-tools is wrapping a static method with an instance method. 

In our Person class, we have the GetZipCode method: 

public class Person { 
    String street; 

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

The Directory.getInstance() method is static. If we extract it to a getDirectory method (in thePerson class) and make this method accessible, we now can mock it.

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

While it's now very easy to mock the getDirectory method using Mockito, it was also easy to mock the Directory.getInstance if we used PowerMockito. Is there an additional reason to introduce a new method? 

If it’s just for the sake of testing - there's no need to do the extraction. Sometimes, however mocking things with power-tools is not easy. Problems appearing in static constructors may require more handling on the test side. It may be easier to wrap in a separate method. 

There are times when extracting helps us regardless of the mocking tool. We can use method extraction to simplify the test, even before we've written it. It's simpler and safer to mock one method, rather than 3 calls.

If our getZipCode method looked like this: 

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(); 

Even with power-tools, faking the Address instance and setting the rest of the behavior settings just for retrieving the directory is quite a lot of work, which means a longer test with a long setup. If we extract a getDirectoryFromAddress method:

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

We get more readable code, and we'll need to mock only one line.

While extracting has its up side, making a method a seam comes with the baggage. If the method is private, and we use power tools to mock it, coupling between test and code is increased. If we make it public, someone can call it. If it's protected, a derived class can call it. Changes for testability is a change of design, for better or worse.

Up next: Creating accessors.


Download the ‘Practical Blueprint to Continuous Delivery’ to learn how Automic Release Automation can help you begin or continue your company’s digital transformation.

Topics:

Published at DZone with permission of Gil Zilberfeld, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}