4
votes

I have a spring boot application that does not much more than listening to an SQS queue via a component "MessageHandler" that has a @SqsListener-annotated method, and start some work when a message arrives.

There is also a boot-starter-web dependency, since we want to fetch health status and metrics via http in production.

Now I wanted to write a module test, that already has an application context and autowires beans. I also found out how to disable the web server that is not needed by the test:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = NONE)

However, the MessageHandler bean also gets instantiated and tries to connect to AWS, which I'd like to prevent.

One solution that works is to have a test implementation in src/test/java with @Primary annotation, whose handleMessage method does NOT have the @SqsListener annotation:

@Component
@Primary
public class TestMessageHandler implements MessageHandler {

    @Override
    public void handleMessage(final NewMessage newMessage) throws Exception {
        return null;
    }
}

But now I'd like to also test the (real) MessageHandler bean, meaning, I'd like Spring to instantiate it and autowire it's dependencies, but I still don't want the @SqsListener annotations to become active, so I can invoke it like this in my test:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = NONE)
public class IntegrationTest {

    @Autowired
    private RealMessageHandler messageHandler;


    @Test
    public void testHandleMessage() throws Exception {
        messageHandler.handleMessage(new NewMessage(...));
    }
}

So what I need is to switch off the magic from the spring-cloud-aws-starter module that sets up the SQS listener for the handleMessage method in the RealMessageHandler.

Any clue how I would do that?

2

2 Answers

5
votes

I had a similar issue and resolved it by mocking the SimpleMessageListenerContainer bean. I plan to do this for multiple integration tests so to those tests more readable I've created a @TestConfiguration class which I then import in the test.

This is my configuration class:


/**
 * Configuration class for integration test that do not need message listening capabilities.
 */
@TestConfiguration
public class MockMessageListenerConfiguration {

  @MockBean
  private SimpleMessageListenerContainer messageListenerContainer;

}

And I use it like this:

@SpringBootTest
@Import({MockMessageListenerConfiguration.class})
class BookingRepositoryIT {...}

After this, the connection refused warnings related to AWS SQS disappeared.

0
votes

For those who want to tests the application without mocking the actual queue send/receive process you can do the following.

In the test autowire the messaging template then disable the listeners like so:

@Autowired
private SimpleMessageListenerContainer simpleMessageListenerContainer;
@Autowired
private QueueMessagingTemplate queueMessagingTemplate;

@Test
public void test() {
String logicalQueueName = "my-queue";
simpleMessageListenerContainer.stop(logicalQueueName);
// code that will trigger message to be inserted into the queue

    Message<?> msg = ((QueueMessagingTemplate) queueMessagingTemplate)
        .receive(logicalQueueName);
// process msg
}

Keep in mind that listeners wont be started automatically afterwards, so you might consider using the DirtiesContext annotoation in your test classs.