Mockito & Junit5

Advanced Mockito Controller Test
A way to capture the information going into the argument of the mock, in other words, to look at the value that is passed in.
When there is a class which takes in a property and then modifies that property.
Then we can do assertions to make sure that modification of the property which has been passed in is actually correct.
OwnerController

public class OwnerController {private final OwnerService ownerService; public String processFindForm(Owner owner, BindingResult result, Model model){ // allow parameterless GET request for /owners to return all records if (owner.getLastName() == null) { owner.setLastName(""); // empty string signifies broadest possible search }// find owners by last name List results = ownerService.findAllByLastNameLike("%"+ owner.getLastName() + "%"); if (results.isEmpty()) { // no owners found result.rejectValue("lastName", "notFound", "not found"); return "owners/findOwners"; } else if (results.size() == 1) { // 1 owner found owner = results.get(0); return "redirect:/owners/" + owner.getId(); } else { // multiple owners found model.addAttribute("selections", results); return "owners/ownersList"; } } }

OwnerControllerTest
@ExtendWith(MockitoExtension.class) class OwnerControllerTest {private static final String OWNERS_CREATE_OR_UPDATE_OWNER_FORM = "owners/createOrUpdateOwnerForm"; private static final String REDIRECT_OWNERS_5 = "redirect:/owners/5"; @Mock OwnerService ownerService; @InjectMocks OwnerController controller; @Mock BindingResult bindingResult; @Test void processCreationFormHasErrors() { //given Owner owner = new Owner(1l, "Jim", "Bob"); given(bindingResult.hasErrors()).willReturn(true); //when String viewName = controller.processCreationForm(owner, bindingResult); //then assertThat(viewName).isEqualToIgnoringCase(OWNERS_CREATE_OR_UPDATE_OWNER_FORM); }@Test void processCreationFormNoErrors() { //given Owner owner = new Owner(5l, "Jim", "Bob"); given(bindingResult.hasErrors()).willReturn(false); given(ownerService.save(any())).willReturn(owner); //when String viewName = controller.processCreationForm(owner, bindingResult); //then assertThat(viewName).isEqualToIgnoringCase(REDIRECT_OWNERS_5); } }

ArgumentCaptor
A way to capture the information going into the argument of the mock, in other words, to look at the value that is passed in.
When there is a class which takes in a property and then modifies that property.
Then we can do assertions to make sure that modification of the property which has been passed in is actually correct.
@ExtendWith(MockitoExtension.class) class OwnerControllerTest {// ...@Captor ArgumentCaptor stringArgumentCaptor; @Test void processFindFormWildcardString() { //given Owner owner = new Owner(1l, "Joe", "Buck"); List ownerList = new ArrayList<>(); // Manually create a captor final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); given(ownerService.findAllByLastNameLike(captor.capture())).willReturn(ownerList); //when String viewName = controller.processFindForm(owner, bindingResult, null); //then assertThat("%Buck%").isEqualToIgnoringCase(captor.getValue()); }@Test void processFindFormWildcardStringAnnotation() { //given Owner owner = new Owner(1l, "Joe", "Buck"); List ownerList = new ArrayList<>(); // In an annotation way given(ownerService.findAllByLastNameLike(stringArgumentCaptor.capture())).willReturn(ownerList); //when String viewName = controller.processFindForm(owner, bindingResult, null); //then assertThat("%Buck%").isEqualToIgnoringCase(stringArgumentCaptor.getValue()); } }

Mockito & Junit5
文章图片

willAnswer
What willAnswer allows us to do is get a hold of the actual invocation of the mock and make some decisions about it.
It compares very closely to ArgumentCaptor but it's kind of combining what we are gonna return from the mock. So the answer functionality
OwnerController
public class OwnerController { private final OwnerService ownerService; public String processFindForm(Owner owner, BindingResult result, Model model){ // allow parameterless GET request for /owners to return all records if (owner.getLastName() == null) { owner.setLastName(""); // empty string signifies broadest possible search }// find owners by last name List results = ownerService.findAllByLastNameLike("%"+ owner.getLastName() + "%"); // Three different test scenarios if (results.isEmpty()) { // no owners found result.rejectValue("lastName", "notFound", "not found"); return "owners/findOwners"; } else if (results.size() == 1) { // 1 owner found owner = results.get(0); return "redirect:/owners/" + owner.getId(); } else { // multiple owners found model.addAttribute("selections", results); return "owners/ownersList"; } } }

OwnerControllerTest
@ExtendWith(MockitoExtension.class) class OwnerControllerTest {@Mock OwnerService ownerService; @InjectMocks OwnerController controller; @Mock BindingResult bindingResult; @Captor ArgumentCaptor stringArgumentCaptor; @BeforeEach void setUp() { given(ownerService.findAllByLastNameLike(stringArgumentCaptor.capture())) .willAnswer(invocation -> { List owners = new ArrayList<>(); String name = invocation.getArgument(0); // Three different test scenarios if (name.equals("%Buck%")) { owners.add(new Owner(1l, "Joe", "Buck")); return owners; } else if (name.equals("%DontFindMe%")) { return owners; } else if (name.equals("%FindMe%")) { owners.add(new Owner(1l, "Joe", "Buck")); owners.add(new Owner(2l, "Joe2", "Buck2")); return owners; }throw new RuntimeException("Invalid Argument"); }); }@Test void processFindFormWildcardFound() { //given Owner owner = new Owner(1l, "Joe", "FindMe"); //when String viewName = controller.processFindForm(owner, bindingResult, Mockito.mock(Model.class)); //then assertThat("%FindMe%").isEqualToIgnoringCase(stringArgumentCaptor.getValue()); assertThat("owners/ownersList").isEqualToIgnoringCase(viewName); }@Test void processFindFormWildcardStringAnnotation() { //given Owner owner = new Owner(1l, "Joe", "Buck"); //when String viewName = controller.processFindForm(owner, bindingResult, null); //then assertThat("%Buck%").isEqualToIgnoringCase(stringArgumentCaptor.getValue()); assertThat("redirect:/owners/1").isEqualToIgnoringCase(viewName); }@Test void processFindFormWildcardNotFound() { //given Owner owner = new Owner(1l, "Joe", "DontFindMe"); //when String viewName = controller.processFindForm(owner, bindingResult, null); //then assertThat("%DontFindMe%").isEqualToIgnoringCase(stringArgumentCaptor.getValue()); assertThat("owners/findOwners").isEqualToIgnoringCase(viewName); } }

【Mockito & Junit5】Debugging the method @Test void processFindFormWildcardFound()
Mockito & Junit5
文章图片

InOrder
Verify the order of the mock interactions,and specify that these mocks need to be called in the specific order.
@Test void processFindFormWildcardFound() { //given Owner owner = new Owner(1l, "Joe", "FindMe"); InOrder inOrder = inOrder(ownerService, model); //when String viewName = controller.processFindForm(owner, bindingResult, model); //then assertThat("%FindMe%").isEqualToIgnoringCase(stringArgumentCaptor.getValue()); assertThat("owners/ownersList").isEqualToIgnoringCase(viewName); // inorder asserts // To make sure that service is called before the model is called inOrder.verify(ownerService).findAllByLastNameLike(anyString()); inOrder.verify(model).addAttribute(anyString(), anyList()); }

    推荐阅读