Apache Camel - Definition has no children on pipeline

Viewed 945

This is my simple route:

@Override
public void configure() {

onException(Exception.class)
    .handled(true)
    .process(exchange -> {
      Exception exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
      log.error(logFormat.error(exchange, exception, exception.getMessage()));
      exchange.getIn().setBody(exception.getMessage());
      exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, HttpStatus.SC_INTERNAL_SERVER_ERROR);
    });

onException(HttpOperationFailedException.class)
    .handled(true)
    .to("direct:handleHttpOperationFailed");

from("direct:myRoute")
    .routeId("myRoute")
    .log("Route start: myRoute")
    .id("startStep")
    .process(
        exchange -> log.info("Doing some processing")
    )
    .id("processingStep")
    .end();
}

Now I want to write some tests:

  1. exception handling case - exception is thrown after "processingStep" and handled in onException
  2. positive scenario - all flow is passing without exceptions

And this is what I have done...

My test annotations:

@RunWith(CamelSpringBootRunner.class)
@UseAdviceWith
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class StarlingFpsInboundWebhookIT {
   ...
}

My two test cases:

  1. With exception scenario - here I use adviceWith to throw exception after "processingStep":
@Test
public void exceptionScenario() throws Exception {

    // Given
    context.getRouteDefinition("myRoute").adviceWith(context, new AdviceWithRouteBuilder() {
      @Override
      public void configure() {
        weaveById("processingStep").after()
            .process(exchange -> {
              throw ExceptionFixtures.createSimpleException();
            })
            .id("exceptionThrowStep");
      }
    });
    context.start();

    String requestBody = ResourcesHelper.read(this.getClass(), "/data/input/request.json");

    // When
    template.send("direct:myRoute", camelExchange -> {...});
}
  1. Success case - here I need to delete previously created "exceptionThrowStep" to not to throw Exception:
@Test
public void successScenario() throws Exception {

    // Given
    context.getRouteDefinition("myRoute").adviceWith(context, new AdviceWithRouteBuilder() {
      @Override
      public void configure() {
        weaveById("exceptionThrowStep").remove();
      }
    });
    context.start();


    String requestBody = ResourcesHelper.read(this.getClass(), "/data/input/request.json");

    // When
    template.send("direct:myRoute", camelExchange -> {...});
}

And when I run those tests (first exception then successful scenario) I get exception in second test:

org.apache.camel.FailedToCreateRouteException: 
Failed to create route myRoute at: 
>>> pipeline -> [[]] <<< 
in route: Route(myRoute)[[From[direct:myRoute]... 
because of Definition has no children on pipeline -> [[]]

Why this exception occurs? I have read many articles but nothing precious found about this case :( What is interesting - when in second test in adviceWith I use replace() instead of remove() everything is working...

My question are:

  1. Is is good approach to test exception handling in route?
  2. Can we reset the route definition between tests to avoid my approach?
1 Answers

I start with your two questions at the end:

  1. Yes, it is good practice to also test the error path of your routes, not only the happy path
  2. Yes, you can and should reset the route definition between tests. Since you run SpringBootTests you can do it with the DirtiesContext annotation (part of Spring Test Framework)

Example:

@Test
@DirtiesContext
public void successScenario() throws Exception {

This resets the Camel route by default after the annotated test has been run.

Your tests must not depend on a specific order to run successful! This leads to flaky test results and lowers your confidence in your tests.

In general your success tests should not need any route modifications since this is the expected processing path. For the error tests you have to inject errors in your routes (as you do it with adviceWith) to simulate processing errors.

Related