Search code examples
javaspring-bootspring-datamockitospring-mvc-test

spring mvc how to test that my service persists entities in post request


So I'm writing this web app with Spring Boot using Spring Data with JPA and Spring MVC and I would like to make mock controller tests. I figured out how to test the get method, but in my controllers post method a new JPA entity is being either persisted or updated with my service. Here is what my controller looks like:

@Controller
@RequestMapping("/registerMember")
public class RegisterMemberController {

    @Autowired
    private MemberService memberService;

    @GetMapping
    public String index(RegisterMemberBean registerMemberBean) {
        return "registerMember";
    }

    @PostMapping
    public String handleSubmit(@Valid RegisterMemberBean registerMemberBean, BindingResult bindingResult, Model model) {
        Member member = registerMemberBean.getMember();
        boolean isRepeatPasswordCorrect = !isRepeatPasswordIncorrect(member.getPassword(), registerMemberBean.getComparePassword());

        if(isAnyErrors(isRepeatPasswordCorrect, !bindingResult.hasErrors())) {
            if(!isRepeatPasswordCorrect) {
                model.addAttribute("isRepeatPasswordIncorrect", true).
                addAttribute("isRepeatPasswordIncorrectMsg", "Passwords don't match");
            }
            return "registerMember";
        }

        boolean errUsername = !memberService.isNoOtherEntityWithUserName(0, member.getUserName());
        boolean errEmail = !memberService.isNoOtherEntityWithEmail(0, member.getEmail());

        if(errUsername || errEmail) {
            if(errUsername) {
                model.addAttribute("isExistingUserName", true).addAttribute("isExistingUserNameMsg", "Already a user with that username");
            } if(errEmail) {
                model.addAttribute("isExistingEmail", true).addAttribute("isExistingEmailMsg", "Already a user with that email");
            }
            return "registerMember";
        }

        getMainService().save(member);
        return redirectTo("index", new RedirectEntity("member", member.getId()));
    }


}

Now in my mock controller test i want to make make sure that my post method does the following:

  1. Reload the page if the BindingResults has any errors
  2. My service persists the member JPA entity in db (if no errors)
  3. Method redirects me to the index page

This is what my (poor) test class looks like so far:

@RunWith(SpringRunner.class)
@TestPropertySource(locations="classpath:application_test.properties")
@WebAppConfiguration
public class RegisterMemberControllerTest {

    private MockMvc mockMvc;

    @MockBean
    private MemberService memberService;

    @MockBean
    private RegisterMemberController controller;

    @Before
    public void init() {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).setViewResolvers(new StandaloneMvcTestViewResolver()).build();
        controller.setMainService(memberService);
    }

    @Test
    public void testIndex() throws Exception {
        mockMvc.perform(get("/registerMember"))
         .andExpect(status().isOk())
         .andExpect(forwardedUrl("registerMember");
    }

    @Test
    public void testHandleSubmit() throws Exception {
        RegisterMemberBean registerMemberBean = new RegisterMemberBean();
        registerMemberBean.setMember(TestFixture.getValidMemberWithoutReferences());

        Member member = TestFixture.getValidMember();

        mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect(status().isOk());
        when(mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect((ResultMatcher) memberService.save(member)).andExpect(forwardedUrl("redirect:/index/member=" + member.getId() + ".html")));

    }
}

to my understanding spring boot uses Mockito. I have some experience with EasyMock but I would like to use the spring defaults as much as possible. Can someone show how to achieve this?


Solution

  • I think there is a little bit of confusion on what should and shouldn't be mocked.

    If I read your question correctly, you are actually trying to Unit Test your RegisterMemberController. Therefore, you most likely should NOT make a mock of that class, but actually test that class.

    I believe that you would be creating fakes/dummies/stubs/mocks/spies of your MemberService, RegisterMemberBean, and BindingResult classes.

    It would be these classes that would be created by your unit test and handed to your controller during the test that will force the testing of the logic that you are interested in proving/disproving.

    FYI, when verifying that the MemberService class was called, that is where you would use a mock. The rest of the classes could either be dummies or stubs.

    Side Note: I would recommend removing the Model parameter from your handleSubmit() method since it doesn't seem to be used anywhere.