4. What’s Unit Test?
Unit tests is the idea that they are tests in
isolation of individual components of software
- Michael C. Feathers
5. What’s Legacy Code?
Legacy code is simply code without tests.
Without tests is bad code. It doesn't matter how well written
it is; it doesn't matter how pretty or object-oriented or well-
encapsulated it is. With tests, we can change the behavior
of our code quickly and verifiably. Without them, we really
don't know if our code is getting better or worse.
- Michael C. Feathers
8. Your Code Looks Like This?
public class Car {
private Engine engine;
public Car() {
engine = new Engine();
}
public void run() {
engine.start();
}
public String status() {
return engine.speed() > 0 ? “Move”: “Stop”;
}
}
9. Your Test Looks Like This?
public class TestCar {
@Test public void move() {
Car car = new Car();
car.run();
assertEquals(“Move”, car.status());
}
}
Are you really do the unit test for Car?
10. Code for Car with Isolation
public class Car {
private IEngine engine;
public Car(IEngine theEngine) {
engine = theEngine;
}
public void run() {
engine.start();
}
public String status() {
return engine.speed() > 0 ? “Move”: “Stop”;
}
}
11. Test for Car with Isolation
public class TestCar {
@Test public void move() {
IEngine engineMock =
new EngineMock(10);
Car car = new Car(engineMock);
car.run();
assertEquals(“Move”, car.status());
}
}
In fact, you don’t care how engine speed is calculated
13. Your Code Looks Like This?
public class Order {
public void addItem(Item item) {
if (isValidItem(item)) {
items.add(item);
}
}
private boolean isValidItem(Item item) {
more than 500 lines code…
}
}
How can I test the private method?
15. Maybe This Code is Better?
public class Order {
private ItemValidator validator;
public Order (ItemValidator
theValidator) {
validator = theValidator;
}
public void addItem (Item item) {
if (validator.
isValidItem(item)) {
…
}
}
}
21. Some New Code needed to be added
public class Customer {
…
public void purchase() {
more than 500 lines of legacy code…
}
…
}
We need to log this customer purchase action after it done.
Can you add unit test for new code with no impact to legacy code?
22. Add new code by Sprout Method
public class Customer {
public void purchase() {
purchaseWithoutLog();
logPurchaseAction();
}
protected void purchaseWithoutLog() {
more than 500 lines of legacy code…
}
private void logPurchaseAction() {
Your new code here…
}
}
23. Your Test May Look Like This
public class TestCustomerPurchaseLog extends
Customer {
@Test public void log() {
Customer customer =
new TestCustomerPurchaseLog();
customer.purchase();
… code to verify the log action …
}
protected void purchaseWithoutLog() {}
}
24. Some other Alternative Ways
• Sprout Method – the Sample Code
• Wrap Method
• If the legacy class is hard to be put into test
harness
o Sprout Class
o Wrap Class
o This is quite useful when you can’t easily isolate the
dependency for legacy class
26. How to Isolate “HttpServletRequest”?
public class ARMDispatcher {
public void populate (HttpServletRequest request) {
String [] values
= request.getParameterValues(pageStateName);
if (values != null && values.length > 0) {
marketBindings.put(
pageStateName + getDateStamp(), values[0]);
}
}
}
27. You only Need to get the Parameter Value
public class ARMDispatcher {
public void populate (ParameterSource source) {
String value =
source.getParameterForName(pageStateName);
if (value != null) {
marketBindings.put(
ageStateName + getDateStamp(), value);
}
}
}
28. I need D, but it’s from A.getB().getC().getD()
29. Your Code may Look like this
public class Customer {
private Orders orders;
public List<String> getAllItemNamesOfLatestOrder(){
List<String> allNames =
new ArrayList<String>();
Item[] items =
orders.getLatestOrder().getAllItems();
for (Item item : items) {
allNames.add(item.getName());
}
return allNames;
}
}
30. You need to Isolate the way to Get Items
public class Customer {
private Orders orders;
public List<String> getAllItemNamesOfLatestOrder(){
List<String> allNames =
new ArrayList<String>();
Item[] items = getAllItemsOfLatestOrder();
for (Item item : items) {
allNames.add(item.getName());
}
return allNames;
}
protected Item[] getAllItemsOfLatestOrder() {
return orders.getLatestOrder().getAllItems();
}
}