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

Legacy Code to Testable Code #4: More Accessors!

DZone's Guide to

Legacy Code to Testable Code #4: More Accessors!

· DevOps Zone
Free Resource

Learn more about how CareerBuilder was able to resolve customer issues 5x faster by using Scalyr, the fastest log management tool on the market. 

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. It continues the last post on accessors.

We talked about “setter” accessors as a mean to inject values. The other side of the coin is when we want to know something has happened inside our object. For example, if internal state has changed, or a non-public method was called. 

In fact, this is like the "if a tree fell in the woods" question: Why do we care if internal state changed?

In legacy code, there are many cases where a class grows large and does many things. If we had separated responsibilities, the result would be possible to assert on another class. Alas, with god objects, things are a bit of a mess. When that happens, our acceptance criteria may move inside: We can either check internal state or internal method calls for its impact.

To check internal state, we can add a “getter” method. Adding a "getter" function is easy, and if it doesn't have logic (and it shouldn’t), it can expose the information without any harm done. If the refactoring tool begs you to add a "setter" you can set it to be private, so no one else uses it.

ROLE REVERSAL

In a funny way, "getter" methods can reverse roles: We can use a "getter" method to inject a value by mocking it. 
So in our getAccount example:

protected Bank getBank() {
    return new Bank();
}public void getAccount() {
    Bank tempBank = getBank();
    ...

By mocking the getBank method we can return a mockBank (according to our tools of choice):

when(testedObject.getBank()).thenReturn(mockBank);


On the other hand, we can verify a call on a "setter" instead of exposing a value. So if ourAccount object has an internal state called balance, instead of exposing it and checking it after the tested operation, we can add a "setter" method, and see if it was called.

verify(account).setBalance(3);

In contrast to injection, when we probe we don't want to expose an object on the stack. It's in the middle of an operation, and therefore not interesting (and hard to examine). If there's an actual case for that, we can use the "setter" method verification option.

In this example, the addMoney function calculates the interimBalance before setting the value back to currentBalance.

public void addMoney(int amount) {
    int interimBalance = currentBalance;
    interimBalance += amount;
    currentBalance = interimBalance;
}

If we want to check the `currentBalance` before the calculation, we can modify the method to:

public void addMoney(int amount) {
    int interimBalance = setInterim(currentBalance);
    interimBalance += amount;
    currentBalance = interimBalance;
}protected void setInterim (int balance){
    return balance;
}

Then in our test we can use verification as a precondition:

verify(account).setInterim(100);

Adding accessors is a solution for a problem that was created before we thought about tests: The design is not modular enough and has many responsibilities. It holds information inside it, and tests (and future clients) cannot access it. If we wrote it "right" the first time, the god class would probably have been written as a set of classes. With our tests in place, we want to get to a modular design.

Tests give us the safety to change the code. So are the automated refactoring tools. We can start the separation even before our tests using the Extract Method refactoring pattern. 

We’re going to discuss it next.


Find out more about how Scalyr built a proprietary database that does not use text indexing for their log management tool, allowing customers to search 1TB of data in under a second. 

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 }}