1

我有以下 Spring MVC 控制器方法:

@RequestMapping(value = "/sendPasswordReset", method = RequestMethod.POST, produces = "text/html")
public String sendPasswordResetInformation(@ModelAttribute @Validated({ ValidationGroups.PasswordReset.class }) PasswordResetInfo passwordResetInfo,
        BindingResult bindingResult, Model model, final RedirectAttributes redirectAttributes, Locale locale) throws InterruptedException, ExecutionException {
    if (preferenceService.isEmailAvailable(passwordResetInfo.getEmail())) {
        bindingResult.rejectValue("email", "controller.preference.email_not_in_system");
    }
    if (bindingResult.hasErrors()) {
        model.addAttribute("passwordResetInfo", passwordResetInfo);
        return "preference/sendPasswordReset";
    }
    redirectAttributes.addFlashAttribute("flashMessage", messageSource.getMessage("controller.preference.password_reset_info_sent", null, locale));
    Future<Void> future = preferenceService.sendPasswordResetInfo(passwordResetInfo.getEmail());//.get();//TODO is ".get()" ugly?
    future.get();//NPE HERE!!
    return "redirect:/preference/sendPasswordReset";
}

这里是实现sendPasswordResetInfo

@Async
@Override
public Future<Void> sendPasswordResetInfo(String email) {
    Assert.hasText(email);
    Member member = memberRepository.findByEmail(email);
    try {
        mailerService.doMailPasswordResetInfo(member);
        return new AsyncResult<Void>(null);
    } catch (MessagingException | MailSendException e) {
        log.error("MessagingException | MailSendException", e);
        throw new MailerException("MessagingException | MailSendException");
    }
}

这是我尝试集成测试控制器方法的方式:

@Test
public void sendPasswordResetShouldHaveNormalInteractions() throws Exception {
    when(preferenceService.isEmailAvailable(anyString())).thenReturn(Boolean.FALSE);
    mockMvc.perform(post("/preference/sendPasswordReset")//
            .param("email", VALID_EMAIL))//
            .andExpect(redirectedUrl("/preference/sendPasswordReset"))//
            .andExpect(flash().attributeExists("flashMessage"))//
            .andExpect(flash().attributeCount(1));
    verify(preferenceService).sendPasswordResetInfo(eq(VALID_EMAIL));
    reset(preferenceService);
}

我在控制器方法中系统地得到了一个 NullPointerException (在测试中),因为这里的未来对象为空:

future.get()

但是,当我使用应用程序(测试之外)时,控制器方法运行良好。

我尝试使用如下同步任务执行器(无济于事):

@Profile(Profiles.TEST)
@Configuration
@EnableAsync
public class FakeAsyncConfiguration implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

我的问题是:

  • 为什么Future在集成测试期间对象始终为空
  • 在集成测试期间如何确保它不为空?
4

0 回答 0