Let's take an example. Let's say that you want to test a Detonator:
public class Detonator {
private Bomb bomb = new AtomicBomb();
public void pushButton() {
// do some stuff to test, and at the end
bomb.explode();
}
}
Now, you have a serious problem, because every time you want to test the detonator, you make the bomb explode, and that is quite costly. So you use dependency injection to solve the problem:
public class Detonator {
private Bomb bomb;
public Detonator(Bomb bomb) {
this.bomb = bomb;
}
public void pushButton() {
// do some stuff to test, and at the end
bomb.explode();
}
}
What does that change for your tests? Everything, because now you can test the detonator this way:
Bomb fakeBomb = new WaterBalloonBomb();
Detonator detonator = new Detonator(fakeBomb);
detonator.pushButton();
// test that the fake bomb has exploded
Now this is quite an unrealistic scenario. But just replace Detonator by BankingService, and Bomb by MainFrameDatabaseAccessor, and you get the idea: needing a populated mainframe database to test the business logic of a service is very cumbersome, makes the tests hard to write, and slow to execute. By using a mock framework, you can create dynamic implementations of the injected class, and verify assertions of this mock object:
MainframeDatabaseAccessor mock = mock(MainFrameDatabaseAccessor.class);
when(mock.findAccount(id1)).thenReturn(sourceAccount);
when(mock.findAccount(id2)).thenReturn(destinationAccount);
BankingService service = new BankingService(mock);
service.transfer(id1, id2, 1000);
// now test that sourceAccount has lost 1000 dollars and destinationAccount has won 1000 dollars
verify(mock).logTransaction(any(Transaction.class));