【问题标题】:Spring LocaleContextHolder not correctly setSpring LocaleContextHolder 未正确设置
【发布时间】:2020-02-13 09:27:04
【问题描述】:

我在使用 Spring LocaleContextHolder 时遇到了一些问题。

我有以下代码:

public void sendPasswordRecoverySmsAsync(String phone) {
    CompletableFuture.runAsync(() -> {
        sendPasswordRecoverySmsSync(phone);
    });
}

public void sendPasswordRecoverySmsSync(String phone) {
    User user = userDao.findByPhone(phone, User.class).orElseThrow(() -> new UserNotFoundException(phone));
    log.info("User found, recovering password");
    user.setUpdateTime(LocalDateTime.now());
    userDao.save(user);

    int otp = codesGenerator.generateOtp(user.getUpdateTime());

    // Sends the SMS.
    Locale locale = LocaleContextHolder.getLocale();
    System.out.println("locale " + locale);
    String appName = messageSource.getMessage("app.name", null, locale);
    String smsContent = messageSource.getMessage("sms.password.recovery", new Object[] { otp }, locale);
    Message message = new TextMessage(appName, phone, smsContent);
    try {
        smsClient.submitMessage(message);
    } catch (NexmoClientException | IOException e) {
        log.error("Error while sending recovery password message to phone number [{}]", phone, e);
        throw new UserActivationException("Error while recovering password for user with phone: " + phone, e);
    }
}

还有这个测试:

@Before
public void setup() {
    LocaleContextHolder.resetLocaleContext();
    Mockito.when(tokenGenerator.generateOtp(Mockito.any())).thenReturn(14);
}

@Test(timeout = 3000)
public void testSendPasswordRecoverySmsAsyncError() throws Exception {
    // Mocks.
    LocaleContextHolder.setLocale(Locale.ENGLISH, true);
    String mockPhone = "333";
    User mockUser = mockModelBuilder.user(true, true);
    Mockito.when(userDao.findByPhone(mockPhone, User.class)).then(r -> {
        // TODO
        return Optional.of(mockUser);
    });
    CountDownLatch latch = new CountDownLatch(1);
    ArgumentCaptor<TextMessage> messageCaptor = ArgumentCaptor.forClass(TextMessage.class);
    Mockito.when(smsClient.submitMessage(messageCaptor.capture())).then(r -> {
        latch.countDown();
        throw new NexmoClientException();
    });

    // Test.
    service.sendPasswordRecoverySmsAsync(mockPhone);
    latch.await();

    // Assertions.
    Assert.assertTrue(true);
    TextMessage actualMessage = messageCaptor.getValue();
    Assert.assertEquals("myApp", actualMessage.getFrom());
    Assert.assertEquals(mockPhone, actualMessage.getTo());
    Assert.assertEquals("Your password recovery code for myApp app is 14", actualMessage.getMessageBody());
}

我希望我的测试输出是“en”,如果我只启动这个,它会正常工作。但是,当我运行所有测试时,输出是“它”。这可能是因为在其他测试中我设置了 ITALIAN 语言环境,或者因为它正在获取系统默认设置。

但是为什么即使我明确地重置它也会出错呢?

【问题讨论】:

  • 它是在本地线程中设置的,您的getLocale 在不同的线程上运行,因此不会看到设置的值并回退到默认值。
  • 第二个标志表示该值应该由子线程继承。此外,这并不能解释为什么如果我只运行单个测试它会起作用
  • 是的,因为它是一个不同的线程。如果您移动线程,它将起作用。还要确保您正在等待线程完成,否则您的拆卸(或再次设置)可能在线程发生更改之前已经清除了一些东西。
  • 等待线程完成是一个不错的选择。实际上,我正在使用闩锁,因此应该注意这一点。您能否详细说明线程将如何影响这一点?我将继承标志设置为真。这意味着即使它在不同的线程上运行,该值也应该根据 Spring 文档从主线程继承。再说一次,如果这是问题所在,即使我只运行一个测试,我也会遇到同样的问题吗?
  • 如果您使用的是闩锁,那么您在myMethod 内的哪个位置使用它?我没看到。能否请您添加实际代码而不是伪代码,否则我们开始得出可能不正确的结论。

标签: java spring junit locale


【解决方案1】:

仅出于测试此类本地化案例的目的,您可能只需要在测试方法中添加以下内容。这基本上会将上下文标记为脏并重新创建它,具体取决于您在测试执行之前还是之后提到上下文是脏的。

@DirtiesContext(methodMode = MethodMode.AFTER_METHOD)
@Test(timeout = 3000)
public void testSendPasswordRecoverySmsAsyncError() throws Exception {...

请参考文档here

希望这会有所帮助,因为它为我解决了类似的问题。

【讨论】:

    猜你喜欢
    • 2015-10-22
    • 1970-01-01
    • 2014-04-10
    • 2013-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-21
    • 2018-06-22
    相关资源
    最近更新 更多