javaspring-bootjunitapache-camelcamel-test

Why is my Camel JUnit test case producer template not able to send a body?


I am trying to test a route which is like this:

from("s3://bucketName")
.process(exchange -> {exchange.getIn().setHeader(Exchange.FILE_NAME,MY_FILE_NAME);})
.log("File download Successful")
.to("file:" + FILE_PATH).routeId("mys3Route");

I have written my test like this:

@Test
public void testFileMovement() throws Exception {
    AdviceWith.adviceWith(context, "mys3Route", a -> {
        a.replaceFromWith("mock:s3Location");
        a.interceptSendToEndpoint("file:" + FILE_PATH).skipSendToOriginalEndpoint()
                .to("mock:anotherLocation");
    });
    MockEndpoint mockedToEndPoint = getMockEndpoint("mock:anotherLocation");
    mockedToEndPoint.setExpectedMessageCount(1);
    template.sendBody("mock:s3Location", "Just Text");
    mockedToEndPoint.assertIsSatisfied();
    Thread.sleep(5000);
}

Whenever I run this as unit test case, I get this error:

org.apache.camel.CamelExecutionException: Exception occurred during >execution on the exchange: Exchange[]

The error seems to be coming up here in: org.apache.camel.impl.engine.DefaultProducerTemplate.extractResultBody(DefaultProducerTemplate.java:591) (which is present in camel dependencies).

Any idea as to what I am doing wrong and how I can rectify it? Any help to resolve and understand this issue is greatly appreciated .


Solution

  • For starters you probably should not replace consumer/From endpoint with MockEndpoint just use direct endpoint. MockEndpoints only support producer endpoints (to) and should not be used as consumer endpoint (from). MockEndpoints are meant to be used as points on your route where you want to do assertions on things like message body, exchange properties, received messages etc.

    Secondly if you're using AdviceWith you should set the isUseAdviceWith to true and start the context manually just before you use template.send methods. How this is set varies a bit if you're using spring boot annotations or not. Example below uses just simple CamelTestSupport.

    Thirdly you rarely if ever need to use intercept on camel tests, use weaveById, weaveByToURI with replace instead. In this case you're better off just fixing how your file-path and file-name is set by using property-placeholders instead. This way you can just use useOverridePropertiesWithPropertiesComponent and TemporaryFolder feature of junit. Also Apache-commons IO FileUtils is pretty handy if you need to read file-contents of a test file or copy something to a test folder.

    Using Thread.Sleep with unit tests is hacky at best and should be avoided. For this case I see no reason why you would use it. RouteId is best placed at the top of the route.

    Example:

    package com.example;
    
    import java.io.File;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.util.Properties;
    
    import org.apache.camel.RoutesBuilder;
    import org.apache.camel.builder.AdviceWithRouteBuilder;
    import org.apache.camel.builder.RouteBuilder;
    import org.apache.camel.component.mock.MockEndpoint;
    import org.apache.camel.test.junit4.CamelTestSupport;
    import org.apache.commons.io.FileUtils;
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.TemporaryFolder;
    
    public class ExampleTest extends CamelTestSupport {
        
        @Rule
        public TemporaryFolder temporaryFolder = new TemporaryFolder();
        File outputFolder;
    
        static final String FILE_NAME = "testfile.txt";
    
        @Test
        public void testFileMovement() throws Exception {
    
            context.getRouteDefinition("mys3Route")
                .adviceWith(context, new AdviceWithRouteBuilder(){
    
                @Override
                public void configure() throws Exception {
    
                    replaceFromWith("direct:start");
                    weaveAddLast()
                        .to("mock:result");
                } 
            });
    
            MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
            resultMockEndpoint.setExpectedMessageCount(1);
    
            startCamelContext();
            template.sendBody("direct:start", "Just Text");
    
            File file = new File(outputFolder, FILE_NAME);
            assertEquals(true, file.exists());
            String fileContents = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
            assertEquals("Just Text", fileContents);
    
            resultMockEndpoint.assertIsSatisfied();
        }
    
        @Override
        protected RoutesBuilder createRouteBuilder() throws Exception {
           
            return new RouteBuilder(){
    
                @Override
                public void configure() throws Exception {
                    
                    from("s3://bucketName")
                        .routeId("mys3Route")
                        .log("File download Successful")
                        .to("file:{{filepath}}?fileName={{filename}}");
                }
            };
        }
    
        @Override
        protected Properties useOverridePropertiesWithPropertiesComponent() {
    
            try {
                outputFolder = temporaryFolder.newFolder("output");
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            Properties properties = new Properties();
            properties.put("filename", FILE_NAME);
            properties.put("filepath", outputFolder.getPath());
    
            return properties;
        }
    
        @Override
        public boolean isUseAdviceWith() {
            return true;
        }   
    }
    
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>${commons-io.version}</version>
    </dependency>