Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MockBean Tests and Multiple Consumers\App Contexts with Spring AMQP

I'm currently facing an issue with testing RabbitMQ consumers with mocks. The issue seems to be that one test class runs with an application context without any mocks, as expected. The next test class to run sets up some mocks that it expects the consumers to use, however when the test runs and a message is sent and it gets picked up by the non-mocked consumers from the application context created for the first test class. As a result my second test fails.

Here is the first test:

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

Second test:

@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());

    }

}

The consumer:

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

    }

}

This brings up two related questions:

  1. How am I supposed to properly do mock bean testing with consumers in Spring?
  2. It looks like Spring is bringing up a new a ApplicationContext for each test class, indicated by the consumer count increasing on the subsequent test runs. It appears that @MockBean affects the cache key of the ApplicationContext (see: Mocking and Spying Beans in Spring Boot) and likely explains why there are multiple application contexts. But how do I stop the consumers in the other stale application contexts from consuming my test messages?

I've bugjar'd this issue here: RabbitMQ MockBean BugJar

like image 314
Ian Dallas Avatar asked Oct 16 '25 01:10

Ian Dallas


1 Answers

Add @DirtiesContext to each test class to shut down the cached context.

like image 114
Gary Russell Avatar answered Oct 18 '25 19:10

Gary Russell