Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing without dependency injection

Code from Spring in Action :

public class DamselRescuingKnight implements Knight {
    private RescueDamselQuest quest;
    public DamselRescuingKnight() {
        this.quest = new RescueDamselQuest();
    }
    public void embarkOnQuest() {
        quest.embark();
    }
}

public class BraveKnight implements Knight {
    private Quest quest;
    public BraveKnight(Quest quest) {
        this.quest = quest;
    }
    public void embarkOnQuest() {
        quest.embark();
    }
}


public class BraveKnightTest {
    @Test
    public void knightShouldEmbarkOnQuest() {
        Quest mockQuest = mock(Quest.class);
        BraveKnight knight = new BraveKnight(mockQuest);
        knight.embarkOnQuest();
        verify(mockQuest, times(1)).embark();
    }
}

I understand the use of dependency injection, which allows us to switch implementation without modifying the depending code.

The book says "terribly difficult to write a unit test ...".

However, I am not able to understand how it will be very difficult for unit-testing without dependency injection! My intuition refuses to co-operate !

Can you start writing junit/unit testing for the class "DamselRescuingKnight" and for any other better example class (without DI), to make me realize the point/stage at which DI makes unit testing easier ?

like image 804
user104309 Avatar asked Jan 06 '23 10:01

user104309


1 Answers

The difficulty in your above example comes when you try to test DamselRescuingKnight. Assume, you want to test that one (see below)

public class DamselRescuingKnight implements Knight {
    private RescueDamselQuest quest;
    public DamselRescuingKnight() {
        this.quest = new RescueDamselQuest();
    }
    public void embarkOnQuest() {
        quest.embark();
    }
}


public class DamselRescuingKnightTest {
    @Test
    public void knightShouldEmbarkOnQuest() {
        DamselRescuingKnight knight = new DamselRescuingKnight ();
        knight.embarkOnQuest();
        // now what?            
    }
}

how can you be sure that knight.embarkOnQuest() does actually do anything? The answer is that you can't because you can't access the quest instance it uses internally.

Now in order to be able to test such a class, you would add a getQuest() method to the Knight, and then also add a isEmbarked() method to Quest. It is also quite fair to say, that this example is very simple, because the knight only calls the quest without parameters, and nothing else. If knight would interact with a quest and also get some weaponary from a Blacksmith, then you would also somehow need to allow access for that. You could probably do all the boilerplate to get that done. But then assume, you're passing parameters to blacksmith - how do you ensure that the passed parameters were correct? Or how do you ensure that the knight gets his/her weapon before going to the quest?

This is where dependency injection comes to the rescue. you can just create mocks (either by using a mock framework, or by implementing your own mocks) so that you can verify that your knight does the expected things.

like image 51
Matthias Avatar answered Jan 08 '23 22:01

Matthias