Dark Matter
Dark Matter

Reputation: 2311

Clarification in Java Code Testing

I have started reading the Spring in Action book.

I have no knowledge of JUnit which I think my doubt is about.

There is a code fragment where the author refers to and says that it is difficult to test:

package com.springinaction.knights;

public classDamselRescuingKnight implements Knight {

    private RescueDamselQuest quest;

    public DamselRescuingKnight() {
       quest = new RescueDamselQuest();
    }

    public voidembarkOnQuest() throwsQuestException {
       quest.embark();
    }
}

The author says that:

It’d be terribly difficult to write a unit test for DamselRescuingKnight. In such a test, you’d like to be able to assert that the quest’s embark() method is called when the knight’s embarkOnQuest() is called. But there’s no clear way to accomplish that here. Unfortunately, DamselRescuingKnight will remain untested.

What does the author mean by this?

Why is the code difficult to test here?

Upvotes: 3

Views: 175

Answers (4)

Shailesh
Shailesh

Reputation: 403

One need to increase accessibility of fields and method of class to test. For example if one is testing a method which is package-private (default) then test cases which are generally in different package will not able to test this method. Therefore it is advised to to change in accessibility of fields to test the method. DamselRescuingKnight class can be tested which is not using DI by modifying the accessibility of RescueDamselQuest field from private to default. Then writing test case using mockito. Here is code for test case

@Test
public void knightShouldEmbarkOnQuest() throws QuestException {


    DamselRescuingKnight knight = new DamselRescuingKnight();
    RescueDamselQuest quest = mock(RescueDamselQuest.class);
    knight.quest = quest;
    knight.embarkOnQuest();
    verify(quest, times(1)).embark();
}

And line which was changed in DamselRescuingKnight class to remove private accessibility

RescueDamselQuest quest;

Upvotes: 0

Pete B.
Pete B.

Reputation: 3286

A common example I give is consider that you want to open a file, parse it, and get a data class out. Most will do something like:

Data openAndParse(String filename) {
  ...openFile
  ...parse
}

By doing it this way, the file open methodology and parse is highly coupled and difficult to test. If you have a problem in open and parse is it with the parse or the open?

By writing JUnit test, you are forced, for simplicity sake, to do something like...

BufferedReader openFile(String filename) {
  ...open file and return reader
}

Data parse(BufferedReader input) {
  ...parse and return data
}

JUnit leads us to a more cohesive solution. We write JUnit test simply by creating a string, constructing a StringReader, and then a BufferedReader. Well guess what? Very similarly we can now use parse to accept input from a variety of sources not just the file.

Upvotes: 2

Dave Newton
Dave Newton

Reputation: 160201

It's difficult to test because the quest implementation cannot be swapped out. Without byte code modification there's no trivial way to see if embark is called.

If you could set the quest implementation in a constructor or setter you could pass in an implementation that can spy on the call to embark.

Upvotes: 0

Jarle Hansen
Jarle Hansen

Reputation: 1993

My initial thought is that it is difficult to test because the "RescureDamselQuest" object is initialized in the constructor. This makes it difficult to for example insert a mock object. A mock object would help you test that the embark() method is called on the "RescueDamselQuest" object.

A better way to solve this can be to either include a parameter in the constructor (usually I prefer this method):

public DamselRescuingKnight(RescueDamselQuest quest){
   this.quest = quest;
}

Or add a setter:

public void setDamselRescuingKnight(RescueDamselQuest quest){
   this.quest = quest;
}

Upvotes: 8

Related Questions