Example: a point-of-sale Sale class
A Sale class has a scan() method that takes the barcode of an item a customer wants to buy. Each scan should show the item’s name and price on a cash register display.
How do we test that the right text reaches the display? If the calls to the register’s display API are buried deep inside Sale, it’s hard to sense the effect. But if we find the spot where the display is updated, we can extract that responsibility.
The refactoring goes in stages:
- Pull the display-talking code out of
Saleinto a dedicated class,ArtR56Display, which holds everything needed to drive the real hardware.Salejust hands it a line of text. Behaviour is unchanged. - Introduce a
Displayinterface that bothArtR56Displayand a test-onlyFakeDisplayimplement. NowSalecan hold either one.
Sale accepts any Display through its constructor:
public interface Display {
void showLine(String line);
}public class Sale {
private Display display;
public Sale(Display display) {
this.display = display;
}
public void scan(String barcode) {
...
String itemLine = item.name() + " " + item.price().asDisplayText();
display.showLine(itemLine);
...
}
}What showLine actually does depends on which display we injected. Give Sale an ArtR56Display and it drives the real register; give it a FakeDisplay and nothing reaches the hardware, but we can read back what would have been shown:
import junit.framework.*;
public class SaleTest extends TestCase {
public void testDisplayAnItem() {
FakeDisplay display = new FakeDisplay();
Sale sale = new Sale(display);
sale.scan("1");
assertEquals("Milk $3.99", display.getLastLine());
}
}The fake itself is deliberately slim — it just remembers the last line it was told to show:
public class FakeDisplay implements Display {
private String lastLine = "";
public void showLine(String line) {
lastLine = line;
}
public String getLastLine() {
return lastLine;
}
}showLine stores the text; getLastLine returns it. That’s barely any behaviour, but it’s enough to verify that Sale sends the right text to the display.
Source
Working Effectively with Legacy Code, Michael Feathers — Chapter 3, Sensing and Separation.