次の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);
}
ここでは将来のオブジェクトがnullであるため、コントローラーメソッドで(テストで)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
統合テスト中にオブジェクトが常に null になるのはなぜですか?- 統合テスト中に null でないことを確認するにはどうすればよいですか?