Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock AMQP consumers in Camel testing?

Say I have the following route:

from(rabbitMQUri)
    .to(myCustomerProcessor)
    .choice()
        .when(shouldGotoA)
            .to(fizz)
        .when(shouldGotoB)
            .to(buzz)
        .otherwise()
            .to(foo);

Let's pretend that myCustomProcessor tunes shouldGotoA and shouldGotoB according to the message consumed from RabbitMQ.

I would like to unit test 3 scenarios:

  1. A "fizz" message is consumed and shouldGotoA is set to true, which executes the first when(...).
  2. A "buzz" message is consumed and shouldGotoB is set to true, which executes the second when(...).
  3. A "foo" message is consumed and the otherwise() is executed.

My question is: how do I mock/stub the RabbitMQ endpoint so that the route executes as it normally will in production, but so that I don't have to actually connect the test to a RabbitMQ server? I need some kind of "mock message" producer.

A code example or snippet would be extremely helpful and very much so appreciated!

like image 717
DirtyMikeAndTheBoys Avatar asked Dec 15 '22 20:12

DirtyMikeAndTheBoys


1 Answers

This is one way of putting together a suitable test.

Firstly define an empty Camel Context with just a ProducerTemplate in it:

<camel:camelContext id="camelContext">
   <camel:template id="producerTemplate" />
</camel:camelContext>

I do this so that when I execute the test, I can control which routes actually start as I don't want all my routes starting during a test.

Now in the test class itself, you'll need references to the producer template and Camel Context. In my case, I'm using Spring and I autowire them in:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/spring/spring-test-camel.xml" })
public class MyTest {

    @Autowired
    private ProducerTemplate producerTemplate;

    @Autowired
    private CamelContext camelContext;

In the test itself, replace the RabbitMQ/ActiveMQ/JMS component in the context with the seda or direct component. eg replace all JMS calls with a seda queue.

camelContext.removeComponent("jms");
camelContext.addComponent("jms", this.camelContext.getComponent("seda"));
camelContext.addRoutes(this.documentBatchRouting);

Now whenever you are reading or writing to a JMS URI, it is actually going to a seda queue. This is similar to injecting a new URI into the component but take less configuration and allows you to add new endpoints to the route without worrying about remembering to inject all the URIs.

Finally in the test, use the the producer template to send a test message:

producerTemplate.sendBody("jms:MyQueue", 2);

You're route should then execute and you can test your expectations.

Two things to note are:

  1. Your transaction boundaries may change, especially if you replace JMS queues with a direct component

  2. If you are testing several routes, you'll have to be careful to remove the route from the Camel Context at the end of the tests for that route.

like image 85
matt helliwell Avatar answered Dec 17 '22 09:12

matt helliwell