The first thing to understand is to know what you want to test.
In your code, you can test the following items:
- Return of
String.format
in the TEXT_OF_A_QUESTION
- Condition within the
if
To first difficulty to test your code is this JOptionPane.showConfirmDialog
. You need to somehow put an interface in place and change its implementation in the test.
For example:
class Beginning {
private OptionPane optionPane = new DefaultOptionPane();
private String namePlate;
private Plate positive;
private Plate negative;
@Override
public void question() {
String question = String.format(TEXT_OF_A_QUESTION, this.namePlate);
int answer = optionPane.showConfirmDialog(null, question, "Question", JOptionPane.YES_NO_OPTION);
if (Integer.valueOf(answer).equals(JOptionPane.YES_OPTION)) {
this.positive.positiveAnswer();
} else {
this.negative.negativeAnswer(this);
}
}
public void setOptionPane(OptionPane o) { this.optionPane = o; }
}
And the implementation of the 2 classes you will need: one with the implementation for the application and the other for the unit test:
public class DefaultOptionPane implements OptionPane {
public int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) {
return JOptionPane.showConfirmDialog(parentComponent,message,title,optionType,messageType);
}
}
public class YesMockOptionPane implements OptionPane {
@Override
public int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) {
return JOptionPane.YES_OPTION;
}
}
By default, your code will always use the DefaultOptionPane
, but in your test you can overwrite it via the method the setOptionPane
.
Then, basically you will need to abuse the methods verify
of the mockito to understand whether the positive
or negative
is called, depending on the return of the optionPane
. But their original code instantiates them internally, this is bad to mock them, because it will also need to expose them. You can do this by creating methods set
for them too, although in practice the ideal was that they had been injected via constructor, hence you can use the same entry point to pass the mocked classes.
I’ll set an example using set
really. It would be something like your test:
@Test
public void test() throws Exception {
OptionPane optionPane = mock(OptionPane.class);
when(optionPane.showConfirmDialog(any(),any(),any(),any()).thenReturn(1); // 1 = Yes, eu acho...
Pane negative = mock(Pane.class);
Pane positive = mock(Pane.class);
Beginning b = new Beginning ("lagosta");
b.setOptionPane(optionPane);
b.setNegative(negative);
b.setPositive(positive);
verify(positive, times(1)).positiveAnswer();
verify(positive, times(0)).negativeAnswer(any());
}
I believe you got the idea. The test of String.format
can be done via mock, waiting for the desired result as method input point showConfirmDialog
, something like that:
when(optionPane.showConfirmDialog(any(),eq("O prato que você pensou é lagosta"),any(),any()).thenReturn(1);
Mocking with an interface is easier than with classes. Use Mockito methods to manage the creation of the interface instance, avoid creating at hand (except if your goal is to run the test without the Mockito)
– Jefferson Quesado