使用 Spring AMQP 的 MockBean 测试和多个 Consumers\App 上下文
MockBean Tests and Multiple Consumers\App Contexts with Spring AMQP
我目前正面临使用模拟测试 RabbitMQ 消费者的问题。正如预期的那样,问题似乎是一个测试 class 运行 没有任何模拟的应用程序上下文。下一个测试 class 到 运行 设置了一些它希望消费者使用的模拟,但是当测试 运行s 和一条消息被发送并且它被非从为第一个测试创建的应用程序上下文中模拟消费者 class。结果我的第二次测试失败了。
这是第一个测试:
@SpringBootTest
public class DemoApplicationTests extends AbstractTestNGSpringContextTests {
@Autowired
private RabbitAdmin rabbitAdmin;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test(priority = 1)
public void contextLoads() {
logger.info("=============== CONSUMERS: " + rabbitAdmin.getQueueProperties(USER_MESSAGING_QUEUE).get(RabbitAdmin.QUEUE_CONSUMER_COUNT));
}
}
第二次测试:
@SpringBootTest
public class UserServiceTests extends AbstractTestNGSpringContextTests {
@Autowired
private UserService userService;
@Autowired
private UserMessageConsumer userMessageConsumer;
@MockBean
@Autowired
private ThirdPartyUserDataClient thirdPartyUserDataClient;
@Autowired
private UserRepository userRepository;
@Autowired
private RabbitAdmin rabbitAdmin;
@Test(priority = 2)
public void createUpdateUserTest() {
logger.info("=============== CONSUMERS: " + rabbitAdmin.getQueueProperties(USER_MESSAGING_QUEUE).get(RabbitAdmin.QUEUE_CONSUMER_COUNT));
String additionalData = org.apache.commons.lang3.RandomStringUtils.random(5);
Mockito.when(thirdPartyUserDataClient.getAdditionalUserData(ArgumentMatchers.anyLong())).thenReturn(additionalData);
User user = new User();
user.setName("Test User");
user.setState(UserState.PENDING);
user = userService.createUser(user);
Assert.assertNotNull(user.getId());
User finalUser = user;
Awaitility.await().until(() -> {
User user2 = userService.getUserById(finalUser.getId());
return finalUser != null && additionalData.equals(user2.getAdditionalData());
});
user.setState(UserState.CREATED);
user = userService.updateUser(user);
Assert.assertEquals(UserState.CREATED, user.getState());
}
}
消费者:
@Component
public class UserMessageConsumer {
private Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String FAILED_TO_GET_ADDITIONAL_DATA = "FAILED_TO_GET_ADDITIONAL_DATA";
@Autowired
private UserService userService;
@Autowired
private ThirdPartyUserDataClient thirdPartyUserDataClient;
public void handleUserCreatedMessage(UserCreatedMessage userCreatedMessage) {
Long userId = userCreatedMessage.getUserId();
User user = userService.getUserById(userId);
if (user != null) {
String additionalData;
try {
additionalData = thirdPartyUserDataClient.getAdditionalUserData(userId);
logger.info("Successfully retrieved additional data [{}] for user [{}].", additionalData, userId);
} catch (HttpClientErrorException ex) {
additionalData = FAILED_TO_GET_ADDITIONAL_DATA;
logger.warn("Failed to retrieve additional data for user [{}].", userId, ex);
}
user.setAdditionalData(additionalData);
userService.updateUser(user);
}
}
}
这提出了两个相关的问题:
- 我应该如何正确地对消费者进行模拟 bean 测试
Spring?
- 看起来 Spring 正在推出一个新的
每个测试 class 的 ApplicationContext,由在后续测试 运行 中增加的消费者计数指示。它出现
@MockBean 影响 ApplicationContext 的缓存键(参见:
Mocking and Spying Beans in Spring Boot) 并且可能解释了为什么有多个应用程序上下文。
但是我如何阻止其他陈旧应用程序上下文中的消费者
正在使用我的测试消息?
我在这里解决了这个问题:RabbitMQ MockBean BugJar
将 @DirtiesContext
添加到每个测试 class 以关闭缓存的上下文。
我目前正面临使用模拟测试 RabbitMQ 消费者的问题。正如预期的那样,问题似乎是一个测试 class 运行 没有任何模拟的应用程序上下文。下一个测试 class 到 运行 设置了一些它希望消费者使用的模拟,但是当测试 运行s 和一条消息被发送并且它被非从为第一个测试创建的应用程序上下文中模拟消费者 class。结果我的第二次测试失败了。
这是第一个测试:
@SpringBootTest
public class DemoApplicationTests extends AbstractTestNGSpringContextTests {
@Autowired
private RabbitAdmin rabbitAdmin;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test(priority = 1)
public void contextLoads() {
logger.info("=============== CONSUMERS: " + rabbitAdmin.getQueueProperties(USER_MESSAGING_QUEUE).get(RabbitAdmin.QUEUE_CONSUMER_COUNT));
}
}
第二次测试:
@SpringBootTest
public class UserServiceTests extends AbstractTestNGSpringContextTests {
@Autowired
private UserService userService;
@Autowired
private UserMessageConsumer userMessageConsumer;
@MockBean
@Autowired
private ThirdPartyUserDataClient thirdPartyUserDataClient;
@Autowired
private UserRepository userRepository;
@Autowired
private RabbitAdmin rabbitAdmin;
@Test(priority = 2)
public void createUpdateUserTest() {
logger.info("=============== CONSUMERS: " + rabbitAdmin.getQueueProperties(USER_MESSAGING_QUEUE).get(RabbitAdmin.QUEUE_CONSUMER_COUNT));
String additionalData = org.apache.commons.lang3.RandomStringUtils.random(5);
Mockito.when(thirdPartyUserDataClient.getAdditionalUserData(ArgumentMatchers.anyLong())).thenReturn(additionalData);
User user = new User();
user.setName("Test User");
user.setState(UserState.PENDING);
user = userService.createUser(user);
Assert.assertNotNull(user.getId());
User finalUser = user;
Awaitility.await().until(() -> {
User user2 = userService.getUserById(finalUser.getId());
return finalUser != null && additionalData.equals(user2.getAdditionalData());
});
user.setState(UserState.CREATED);
user = userService.updateUser(user);
Assert.assertEquals(UserState.CREATED, user.getState());
}
}
消费者:
@Component
public class UserMessageConsumer {
private Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String FAILED_TO_GET_ADDITIONAL_DATA = "FAILED_TO_GET_ADDITIONAL_DATA";
@Autowired
private UserService userService;
@Autowired
private ThirdPartyUserDataClient thirdPartyUserDataClient;
public void handleUserCreatedMessage(UserCreatedMessage userCreatedMessage) {
Long userId = userCreatedMessage.getUserId();
User user = userService.getUserById(userId);
if (user != null) {
String additionalData;
try {
additionalData = thirdPartyUserDataClient.getAdditionalUserData(userId);
logger.info("Successfully retrieved additional data [{}] for user [{}].", additionalData, userId);
} catch (HttpClientErrorException ex) {
additionalData = FAILED_TO_GET_ADDITIONAL_DATA;
logger.warn("Failed to retrieve additional data for user [{}].", userId, ex);
}
user.setAdditionalData(additionalData);
userService.updateUser(user);
}
}
}
这提出了两个相关的问题:
- 我应该如何正确地对消费者进行模拟 bean 测试 Spring?
- 看起来 Spring 正在推出一个新的 每个测试 class 的 ApplicationContext,由在后续测试 运行 中增加的消费者计数指示。它出现 @MockBean 影响 ApplicationContext 的缓存键(参见: Mocking and Spying Beans in Spring Boot) 并且可能解释了为什么有多个应用程序上下文。 但是我如何阻止其他陈旧应用程序上下文中的消费者 正在使用我的测试消息?
我在这里解决了这个问题:RabbitMQ MockBean BugJar
将 @DirtiesContext
添加到每个测试 class 以关闭缓存的上下文。