Working Effectively with Legacy Code: Chapter 9 Summary

Back at it again, this week we will cover chapter 9 of the book titled "I can't get this class into a test harness".

Sometimes it's just hard. But if it were easy then everyone would do it!!

Okay, I'm sorry that sounds a bit cheesy.

Anyways this chapter goes through a series of examples that highlight the most common problems with getting a class into a test harness.

These problems include:

  1. Objects of the class can’t be created easily.
  2. The test harness won’t easily build with the class in it.
  3. The constructor we need to use has bad side effects.
  4. Significant work happens in the constructor, and we need to sense it.

In the book there are 6 examples, he uses. For the sake of saving your precious time, I only summarised one.


Hi, I'm summarizing the book "Working Effectively with Legacy Code". If you are interested in the book, feel free to check it out here.

Working Effectively with Legacy Code
Get more out of your legacy systems: more performance, functionality, reliability, and manageability Is your code easy to change? Can you get nearly instantaneous feedback when you do change it? … - Selection from Working Effectively with Legacy Code [Book]

Irritating Parameter

This example illustrates a problem which seems simple at first but gets very irritating.

In a billing system, we have an untested Java class named CreditValidator.

public class CreditValidator
{
	public CreditValidator(RGHConnection connection,
                           CreditMaster master,
                           String validatorID) {
		... 
	}
    
    Certificate validateCustomer(Customer customer)
            throws InvalidCredit { 
            ... 
    }
     
    ...
}

This class essentially checks whether a credit card is valid or not. If not then it throws an InvalidCredit exception.

Our mission is to add a new method called getValidPercent which will tell us the percentage of successful validateCustomer calls we've made over the life of the validator.

But first, we have to get this class under tests. The first step is to see whether we can get a test harness.

public void testCreate() {
	CreditValidator validator = new CreditValidator();
}

This is a construction test. We don't have any assertions we just try to create the object.

The current test will fail because we didn't add any parameters so let's try to create objects of RGHConnection and CreditMaster.

public class RGHConnection
{
    public RGHConnection(int port, String Name, string passwd)
            throws IOException {
... }
}
public class CreditMaster
{
    public CreditMaster(String filename, boolean isLocal) {
        ...
} }
This is the internals of the RGHConnection and CreditMaster
public void testCreate() throws Exception {
    RGHConnection connection = new RGHConnection(DEFAULT_PORT, "admin", "rii8ii9s");
    CreditMaster master = new CreditMaster("crm2.mas", true);
    CreditValidator validator = new CreditValidator(connection, master, "a");
}

Seems simple right?

Well, we have made a grave error.

Our RGHconnection object connects to an actual server.

This is the irritating parameter.  

To fix that, we have to create a fake RGHConnection object and make CreditValidator believe that it's talking to a real one.

To do that we have to refactor RGHConnection to make it use an interface called IRGHConnection and using this interface we create a fake class called FakeConnection.

public class FakeConnection implements IRGHConnection
{
    public RFDIReport report;
    public void connect() {}
    public void disconnect() {}
    public RFDIReport RFDIReportFor(int id) { return report; }
    public ACTIOReport ACTIOReportFor(int customerID) { return null; }
}

Using this class we can write our tests.

void testNoSuccess() throws Exception {
    CreditMaster master = new CreditMaster(“crm2.mas”, true);
    IRGHConnection connection = new FakeConnection();
    CreditValidator validator = new CreditValidator(
                                        connection, master, “a”);
    connection.report = new RFDIReport(...);
    Certificate result = validator.validateCustomer(new Customer(...));
    assertEquals(Certificate.VALID, result.getStatus());
}

Conclusion

This chapter primarily had very useful examples.

Unfortunately, I couldn't showcase them all but if you are interested then make sure to get the book.

Thanks for reading.