I am very new with Mockito and I don't get the following example (classes were provided, only test to write) and how to solve it. What I try to do is use a test double for the supplier so that we can control the returned greeting in the test and assert that the GreetingService does not modify the greeting message in any way. Then assert that the returned greeting string is equal to "Hello Andy.".
public class Greeting {
private final String template;
public Greeting(String template) {
this.template = template;
}
public String forName(String world) {
return String.format(template, world);
}
}
@Component
public class GreetingService {
private final Supplier<Greeting> greetingSupplier;
public GreetingService(Supplier<Greeting> greetingSupplier) {
this.greetingSupplier = greetingSupplier;
}
public String greet(String name) {
return greetingSupplier.get().forName(name);
}
}
@Component
public class RandomGreetingSupplier implements Supplier<Greeting> {
private final List<Greeting> greetings = Arrays.asList(
new Greeting("Hello %s."),
new Greeting("Hi %s!"),
);
private final Random random = new Random();
@Override
public Greeting get() {
return greetings.get(random.nextInt(greetings.size()));
}
}
@SpringBootTest
public class GreetingServiceTest {
@Autowired
GreetingService greetingService;
@MockBean
Supplier<Greeting> greetingSupplier;
@Test
void getGreetingForPerson() {
String name = "Andy";
// that test cannot know which greeting will be returned by the supplier
// WHY IS IT NULLPOINTEREXCEPTION AFTER INITIALIZING @MockBean
//String greeting = greetingService.greet(name);
//assertThat(greeting).contains(name);
// WROTE SUCH TEST HERE -> NullPointerException WHY?
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
assertThat(greetingSupplier.equals("Hello Andy."));
// THIS IS WORKING & TEST PASSED BUT I GUESS ITS WRONG?
Mockito.when(greetingSupplier.get()).thenReturn(new Greeting("Hello %s."));
assertThat(greetingSupplier.equals("Hello Andy."));
}
}
CodePudding user response:
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
You can't chain calls like that, you need to produce intermediate results, like
Supplier<Greeting> supplier = mock(Supplier.class);
Mockito.when(supplier).forName().thenReturn("Hello %s.");
Mockito.when(greetingSupplier.get()).thenReturn(supplier);
For dependency injection, you need to create the subject under test with the mocked Supplier. You can do that in a @Before
method for example.
CodePudding user response:
Your mocking is wrong.
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
You mocked Supplier<Greeting>
and the default behavior is to return null
. So when you call greetingSupplier.get()
in your first line it returns null
. You directly chain forName
which nou basicall is null.forName
which leads to an error.
Your second part is actually (kind of) correct.
Mockito.when(greetingSupplier.get()).thenReturn(new Greeting("Hello %s."));
You now properly return a response from greetingSupplier.get()
. Instead of chaining the call.
However I would argue that your excercise is wrong. Why? When using a Supplier<?>
in Spring it actually is a lazy beanFactory.getBean
call. You can lazily inject dependencies this way. You should have a mock for Greeting
which returns a hardcoded String
which you can check.