Search code examples
javamockingapache-camelcamel-test

Camel : Mock and return value from component in route


I have the following route in my service:

public void configure() {

    /*
     * Scheduled Camel route to produce a monthly report from the audit table. 
     * This is scheduled to run the first day of every month.
     */

    // @formatter:off
    from(reportUri)
        .routeId("monthly-report-route")
        .log("Audit report processing started...")
        .to("mybatis:updateProcessControl?statementType=Update")
        .choice()
            /*
             * If the rows updated is 1, this service instance wins and can run the report.
             * If the rows updated is zero, go to sleep and wait for the next scheduled run.  
             */
            .when(header("CamelMyBatisResult").isEqualTo(1))
                .process(reportDateProcessor)
                .to("mybatis:selectReport?statementType=SelectList&consumer.routeEmptyResultSet=true")
                .process(new ReportProcessor())
                .to("smtp://smtpin.tilg.com?to=" 
                        + emailToAddr 
                        + "&from=" + emailFromAddr )
                .id("RecipientList_ReportEmail")
        .endChoice()
    .end();
    // @formatter:on
}

When i try to run a test on this it gives me an error stating camel cannot auto create component mybatis. I'm inexperienced with testing camel routes so im not entirely sure where to go with this. The first mybatis call updates a row in a table, which is not under test so i'd like to do something like when the endpoint is hit, return the CamelMyBatisResult header with a value of 1. The second mybatis endpoint should return a hashmap(empty for first test, populated for the second). How do i go about implementing a when/then kind of mechanism with camel testing? I've looked at the mock endpoint camel docs but i can't figure out how to apply that and have it return a value to the exchange, then continue with the route(the end result of the test is to check that an email with or without an attachment is sent)

EDIT: tried using both replace().set* methods and replacing the mybatis endpoints with a call to inline processors:

@Test
public void test_reportRoute_NoResultsFound_EmailSent() throws Exception {
    List<AuditLog> bodyList = new ArrayList<>();
    context.getRouteDefinition("monthly-report-route").adviceWith(context, 
            new AdviceWithRouteBuilder() {
                @Override
                public void configure() throws Exception {
                    replaceFromWith(TEST);
                    weaveById("updateProcControl").replace()
                        .process(new Processor() {
                            @Override
                            public void process(Exchange exchange) throws Exception {
                                exchange.getIn().setHeader("CamelMyBatisResult", 1);
                            }
                        });
                    weaveById("selectReport").replace()
                        .process(new Processor() {
                            @Override
                            public void process(Exchange exchange) throws Exception {
                                exchange.getIn().setBody(bodyList);
                            }
                        });
                    weaveById("RecipientList_reportEmail").replace()
                        .to("smtp://localhost:8083"
                                +"?to=" + "[email protected]"
                                +"&from=" + "[email protected]");
                }
    });
    ProducerTemplate prod = context.createProducerTemplate();
    prod.send(TEST, exch);

    assertThat(exch.getIn().getHeader("CamelMyBatisResult"), is(1));
    assertThat(exch.getIn().getBody(), is(""));
}

Thus far, the header is still null, as is the body(TEST variable is a direct component)


Solution

  • If you want to put in hardcoded responses, it is easier to do an adviceWith on your routes. See here: http://camel.apache.org/advicewith.html

    Basically, add an id to each endpoint or .to(). Then your test perform an adviceWith and then replace that .to() with some hardcoded response. It can be a map, string or anything else you want and it will be replaced. For example:

    context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
        @Override
        public void configure() throws Exception {
            // weave the node in the route which has id = bar
            // and replace it with the following route path
            weaveById("bar").replace().multicast().to("mock:a").to("mock:b");
        }
    });
    

    Note, it says in the documentation that you need to override the isAdviceWith method and start and stop the camelContext manually.

    Let us know if you run into issues with it. It can be a bit tricky to get started but once you get the hang of it it is actually very powerful to mock responses.

    Here is an example that adds a body to simple expression when you do adviceWith..

    context.getRouteDefinition("yourRouteId").adviceWith(context, new AdviceWithRouteBuilder() {
      @Override
      public void configure() throws Exception {
        weaveById("yourEndpointId").replace().setBody(new ConstantExpression("whateveryouwant"
         ));
      }
    
    });