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);
}

ここでは将来のオブジェクトが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 でないことを確認するにはどうすればよいですか?
4

0 に答える 0