How does dependecy injection allow for easier testing - need a java example to grasp the concept?

StackOverflow https://stackoverflow.com/questions/23618509

  •  21-07-2023
  •  | 
  •  

Question

So I understand what dependecy injection is:

Instead of having your objects creating a dependency or asking a factory object to make one for them, you pass the needed dependencies in to the constructor or via property setters, and you make it somebody else's problem (an object further up the dependency graph, or a dependency injector that builds the dependency graph). A dependency as I understand here is any other object the current object needs to hold a reference to

great. Now how does this make testing easier? It makes sense that it would but I'm trying to think of an example that would solidify the concept in my head. Can you provide an example of test code becoming easier than it would have been if we hardwired the dependency in the class?

Thank you in advance.

Was it helpful?

Solution

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));

OTHER TIPS

Take a periodical batch process writing new facts to the database and doing checks against NOW - 7 DAYS. If you prepare test data, that "NOW" is painful. Initialising that NOW outside the class is one option: The database connection would make an even more regular case for DI.

class UpdateBatch { // Class to test
    Date now; // Java 7
    Instant now; // Java 8

    Connection connection;
}

Here one could do without DI:

class ProductionUpdateBatch extends UüdateBatch { // No  longer tested
    ...
    now = new Date();
    now = Instant.now();
    connection = ...
}

but with DI one may keep the actual values declarative, in an XML source or so. Especially you may define it outside the application, in the application container.

For a more abstract conceptual perspective you might look into inversion of control.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top