使用Spring MockMvc *类测试Spring MVC控制器方法

时间:2013-04-26 11:18:02

标签: unit-testing spring-mvc mocking

我正在尝试测试以下Spring mvc控制器方法:

@RequestMapping(value = "/preferences/email", method = RequestMethod.POST, produces = "text/html")
public String modifyEmail(@ModelAttribute @Validated({ Validation.EmailModification.class }) EmailInfo emailInfo, BindingResult bindingResult, Model model, Locale locale) {
    Member member = memberService.retrieveCurrentMember();
    if (!preferencesService.isEmailAvailable(emailInfo.getEmail())) {
        if (member.getEmail().equals(emailInfo.getEmail())) {
            bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.same_email", null, locale)));
        } else {
            bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.email_already_used", null, locale)));
        }
    }
    if (bindingResult.hasErrors()) {
        model.addAttribute("emailInfo", emailInfo);
        return "preferences";
    }
    preferencesService.modifyEmail(member, emailInfo.getEmail());
    return "redirect:/preferences/email";
}

这是EmailInfo bean:

@RooEquals
@RooJavaBean
public class EmailInfo {

    @NotNull(groups = { Validation.EmailModification.class })
    @Pattern(regexp = "^[_a-z0-9-]+(\\.[_a-z0-9-]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)+$", groups = { Validation.EmailModification.class })
    private String email;

    private boolean activated;

    private String token;
}

这是测试类:

@ContextConfiguration
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class PreferenceControllerTest {

    @Autowired
    private WebApplicationContext ctx;

    private MockMvc mockMvc;

    @Autowired
    private MemberService memberService;

    @Autowired
    private PreferencesService preferencesService;

    @Autowired
    private MemberRepository memberRepository;

    @Autowired
    private SigninService signinService;

    @Autowired
    private MessageSource messageSource;

    @Before
    public void setup() {
        mockMvc = webAppContextSetup(ctx).build();
        Member currentMember = new Member();
        currentMember.setEmail("currentMember@example.com");
        when(memberService.retrieveCurrentMember()).thenReturn(currentMember);
        when(preferencesService.isEmailAvailable("notAvailable@example.com")).thenReturn(Boolean.FALSE);
    }

    @Test
    public void test() throws Exception {
        mockMvc.perform(post("/preferences/email")//
                .param("email", "newEmail@example.com"))//
                .andDo(print()).andExpect(model().attributeHasNoErrors("emailInfo", "email"));
    }

    @Configuration
    public static class testConfiguration {
        @Bean
        public PreferenceController preferenceController() {
            return new PreferenceController();
        }

        @Bean
        public PreferencesService preferenceService() {
            return mock(PreferencesService.class);
        }

        @Bean
        public MemberService memberService() {
            return mock(MemberService.class);
        }

        @Bean
        public MemberRepository memberRepository() {
            return mock(MemberRepository.class);
        }

        @Bean
        public SigninService signinService() {
            return mock(SigninService.class);
        }

        @Bean
        public MessageSource messageSource() {
            return mock(MessageSource.class);
        }

    }
}

奇怪的是我得到以下输出:

MockHttpServletRequest:
         HTTP Method = POST
         Request URI = /preferences/email
          Parameters = {email=[newEmail@example.com]}
             Headers = {}

             Handler:
                Type = com.bignibou.controller.PreferenceController

               Async:
   Was async started = false
        Async result = null

  Resolved Exception:
                Type = null

        ModelAndView:
           View name = preferences
                View = null
           Attribute = emailInfo
               value = com.bignibou.controller.helpers.EmailInfo@9a56c123
              errors = [Field error in object 'emailInfo' on field 'email': rejected value [null]; codes []; arguments []; default message [null]]

            FlashMap:

MockHttpServletResponse:
              Status = 200
       Error message = null
             Headers = {}
        Content type = null
                Body = 
       Forwarded URL = preferences
      Redirected URL = null
             Cookies = []

测试因上述输出而失败,我不知道为什么。我希望测试通过,因为电子邮件地址可用。

有人可以帮忙吗?

编辑1

以下情况也不起作用:

@Before
    public void setup() {
        mockMvc = webAppContextSetup(ctx).build();
        Member currentMember = new Member();
        currentMember.setEmail("currentMember@example.com");
        when(memberService.retrieveCurrentMember()).thenReturn(currentMember);
        when(preferencesService.isEmailAvailable(eq("notAvailable@example.com"))).thenReturn(Boolean.FALSE);
        when(preferencesService.isEmailAvailable(eq("newEmail@example.com"))).thenReturn(Boolean.TRUE);
    }

编辑2

我能够使用上面的编辑1 加上下面的测试:

@Test
    public void test() throws Exception {
        mockMvc.perform(post("/preferences/email")//
                .param("email", "available@example.com"))//
                .andDo(print())//
                .andExpect(model().attributeHasNoErrors("emailInfo"));
    }

2 个答案:

答案 0 :(得分:2)

有了这个:

.param("email", "newEmail@example.com"))//

您正在为字符串值设置请求参数。但是,您尚未显示从String到EmailInfo的转换。

在您的测试中,您正在检查名为email的emailInfo字段。

我不确定这是为了什么?

when(preferencesService.isEmailAvailable("notAvailable@example.com")).thenReturn(Boolean.FALSE);

应该做什么,你已经使用autowired注入了您的preferenceService。

答案 1 :(得分:1)

更新回答评论。

在控制器中

尝试

String email=emailInfo.getEmail(); if(!preferencesService.isEmailAvailable(email))){代替if (!preferencesService.isEmailAvailable(emailInfo.getEmail())) {

不确定,只是一个可能的解决方案

或尝试

    when(preferencesService.isEmailAvailable(eq("newEmail@example.com"))).thenReturn(Boolean.TRUE);
when(preferencesService.isEmailAvailable(eq("notAvailable@example.com"))).thenReturn(Boolean.FALSE);

您是否使用Mockito实施嘲弄?


我不是100%肯定,但这是我如何理解你的代码。

when(preferencesService.isEmailAvailable("notAvailable@example.com")).thenReturn(Boolean.FALSE);

如果preferencesService.isEmailAvailable返回true,那么您强行返回false中的mock exercise

因此,当mock exercise preferencesService.isEmailAvailable总是返回false时。

现在在Controller

if (!preferencesService.isEmailAvailable(emailInfo.getEmail())) {
        if (member.getEmail().equals(emailInfo.getEmail())) {
            bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.same_email", null, locale)));
        } else {
            bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.email_already_used", null, locale)));
        }
    }

如果preferencesService.isEmailAvailablefalse,则!将其设为true,因此代码将始终位于if阻止内,您将获得Field Error ,因此Test fails