Repository: camel Updated Branches: refs/heads/master f43c81643 -> 6abfa3203
Camel CDI test Asciidoc documentation Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/6abfa320 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/6abfa320 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/6abfa320 Branch: refs/heads/master Commit: 6abfa320395f24a64543621b98de8ff3c1669898 Parents: f43c816 Author: Antonin Stefanutti <anto...@stefanutti.fr> Authored: Wed Mar 9 18:33:46 2016 +0100 Committer: Antonin Stefanutti <anto...@stefanutti.fr> Committed: Wed Mar 9 18:33:46 2016 +0100 ---------------------------------------------------------------------- .../camel-cdi/src/main/docs/cdi-testing.adoc | 674 +++++++++++++++++++ docs/user-manual/en/SUMMARY.md | 2 + 2 files changed, 676 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/6abfa320/components/camel-cdi/src/main/docs/cdi-testing.adoc ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/docs/cdi-testing.adoc b/components/camel-cdi/src/main/docs/cdi-testing.adoc new file mode 100644 index 0000000..98bb7ba --- /dev/null +++ b/components/camel-cdi/src/main/docs/cdi-testing.adoc @@ -0,0 +1,674 @@ +[[CDITesting-CDITesting]] +CDI Testing +~~~~~~~~~~~ + +http://camel.apache.org/testing.html[Testing] is a crucial part of any +development or integration work. In case you're using the Camel CDI +integration for your applications, you have a number of options to ease +testing. + +You can use CDI for IoC and the Camel testing endpoints like +`DataSet`, `Mock`, `Test` and testing API like `AdviceWith` +and `NotifyBuilder` to create sophisticated integration/unit tests that +are easy to run and debug inside your IDE. + +There are a number of supported approaches for testing with CDI in +Camel: + +[width="100%",cols="1,1,4",options="header",] +|======================================================================= +|Name |Testing Frameworks Supported |Description +|<<CDITesting-CamelCDITest,Camel CDI Test>> a| +* JUnit 4 + + a| +*Available as of Camel 2.17* + +The Camel CDI test module (`camel-test-cdi`) provides a JUnit runner +that bootstraps a test environment using CDI so that you don't have to +be familiar with any CDI testing frameworks and can concentrate on the +testing logic of your Camel CDI applications. + +|<<CDITesting-Arquillian,Arquillian>> a| +* JUnit 4 +* TestNG 5 + + |http://arquillian.org/[Arquillian] is a testing platform that handles +all the plumbing of in-container testing with support for a wide range +of http://arquillian.org/modules/[target containers]. Arquillian can be +configured to run your test classes in _embedded_ (in JVM CDI), +_managed_ (a real Web server or Java EE application server instance +started in a separate process) or _remote_ (the lifecycle of the +container isn't managed by Arquillian) modes. You have to create the +System Under Test (SUT) in your test classes using +http://arquillian.org/guides/shrinkwrap_introduction/[ShrinkWrap +descriptors]. The benefit is that you have a very fine-grained control +over the application configuration that you want to test. The downside +is more code and more complex _classpath_ / class loading structure. + +|<<CDITesting-PAXExam,PAX Exam>> a| +* JUnit 4 +* TestNG 6 + + |https://ops4j1.jira.com/wiki/display/PAXEXAM4[PAX Exam] lets you test +your Camel applications in OSGi, Java EE or standalone CDI containers +with the ability to finely configure your System Under Test (SUT), +similarly to Arquillian. You can use it to test your Camel CDI +applications that target OSGi environments like Karaf with +https://ops4j1.jira.com/wiki/display/PAXCDI/Pax+CDI[PAX CDI], but you +can use it as well to test your Camel CDI applications in standalone +https://ops4j1.jira.com/wiki/display/PAXEXAM4/CDI+Containers[CDI +containers], +https://ops4j1.jira.com/wiki/display/PAXEXAM4/Web+Containers[Web +containers] and +https://ops4j1.jira.com/wiki/display/PAXEXAM4/Java+EE+Containers[Java EE +containers]. +|======================================================================= + +[[CDITesting-CamelCDITest]] +Camel CDI Test +^^^^^^^^^^^^^^ + +With this approach, your test classes use the JUnit runner provided in +Camel CDI test. This runner manages the lifecycle of a standalone CDI +container and automatically assemble and deploy the System Under Test +(SUT) based on the _classpath_ into the container. + +It deploys the test class as a CDI bean so that dependency injection and +any CDI features is available within the test class. + +Maven users will need to add the following dependency to +their `pom.xml` for this component: + +[source,xml] +---- +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test-cdi</artifactId> + <scope>test</test> + <version>x.x.x</version> + <!-- use the same version as your Camel core version --> +</dependency> +---- + +Here is a simple unit test using the `CamelCdiRunner`: + +[source,java] +---- +@RunWith(CamelCdiRunner.class) +public class CamelCdiRunnerTest { + + @Inject + CamelContext context; + + @Test + public void test() { + assertThat("Camel context status is incorrect!", + context.getStatus(), + is(equalTo(ServiceStatus.Started))); + } +} +---- + +CDI injection is also available for test method parameters, e.g.: + +[source,java] +---- +@RunWith(CamelCdiRunner.class) +public class CamelCdiRunnerTest { + + @Test + public void test(@Uri("direct:foo") ProducerTemplate producer) { + producer.sendBody("bar"); + } +} +---- + +Camel CDI test provides the `@Order` annotation that you can use to +execute the test methods in a particular sequence, e.g.: + +[source,java] +---- +@RunWith(CamelCdiRunner.class) +public class CamelCdiRunnerTest { + + @Test + @Order(1) + public void firstTestMethod() { + } + + @Test + @Order(2) + public void secondTestMethod() { + } +} +---- + +One CDI container is bootstrapped for the entire execution of the test +class. + +Besides, the test class is deployed as a CDI bean, so that you can +control how the runner instantiate the test class, either one test class +instance for each test method (the default, depending on the _built-in_ +default `@Dependent` CDI scope), or one test class instance for the +entire test class execution using the `@ApplicationScoped` scope, e.g.: + +[source,java] +---- +@ApplicationScoped +@RunWith(CamelCdiRunner.class) +public class CamelCdiRunnerTest { + + int counter; + + @Test + @Order(1) + public void firstTestMethod() { + counter++; + } + + @Test + @Order(2) + public void secondTestMethod() { + assertEquals(counter, 1); + } +} +---- + +[[CDITesting-Arquillian]] +Arquillian +^^^^^^^^^^ + +With this approach, you use the JUnit runner or TestNG support provided +by Arquillian to delegate the bootstrap of the CDI container. You need +to declare a `@Deployment` method to create your application +configuration to be deployed in the container using +http://arquillian.org/guides/shrinkwrap_introduction/[ShrinkWrap +descriptors], e.g.: + +[source,java] +---- +@RunWith(Arquillian.class) +public class CamelCdiJavaSeTest { + + @Deployment + public static Archive deployment() { + return ShrinkWrap.create(JavaArchive.class) + // Camel CDI + .addPackage(CdiCamelExtension.class.getPackage()) + // Test classes + .addPackage(Application.class.getPackage()) + // Bean archive deployment descriptor + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Inject + CamelContext context; + + @Test + public void test() { + assertThat("Camel context status is incorrect!", + context.getStatus(), + is(equalTo(ServiceStatus.Started))); + } +} +---- + +In that example, you can use any Java SE Arquillian embedded container +adapter, like the +http://arquillian.org/modules/arquillian-weld-se-embedded-1.1-container-adapter/[Weld +embedded container adapter] e.g. with Maven you need that complete set +of dependencies: + +[source,xml] +---- +<dependencies> + + <dependency> + <groupId>org.jboss.arquillian.junit</groupId> + <artifactId>arquillian-junit-container</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + <artifactId>shrinkwrap-descriptors-depchain</artifactId> + <type>pom</type> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.jboss.arquillian.container</groupId> + <artifactId>arquillian-weld-se-embedded-1.1</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.jboss.weld</groupId> + <artifactId>weld-core</artifactId> + <scope>test</scope> + </dependency> + +</dependencies> +---- + +Using ShrinkWarp Descriptors, you have a complete control over the +configuration and kind of Camel CDI applications you want to test. For +example, to test a Camel CDI application that uses the Camel +link:rest-dsl.html[REST DSL] configured with the +link:servlet.html[Servlet component], you need to create a Web archive, +e.g.: + +[source,java] +---- +@RunWith(Arquillian.class) +public class CamelCdiWebTest { + + @Deployment + public static Archive<?> createTestArchive() { + return ShrinkWrap.create(WebArchive.class) + .addClass(Application.class) + .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml")) + .setWebXML(Paths.get("src/main/webapp/WEB-INF/web.xml").toFile()); + } + + @Test + @RunAsClient + public void test(@ArquillianResource URL url) throws Exception { + assertThat(IOHelper.loadText(new URL(url, "camel/rest/hello").openStream()), + is(equalTo("Hello World!\n"))); + } +} +---- + +In the example above, you can use any Arquillian Web container adapter, +like +the http://arquillian.org/modules/arquillian-jetty-embedded-9-container-adapter/[Jetty +embedded container adapter] e.g. with Maven you need the +complete following set of dependencies: + +[source,xml] +---- +</dependencies> + + <dependency> + <groupId>org.jboss.arquillian.junit</groupId> + <artifactId>arquillian-junit-container</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.jboss.arquillian.testenricher</groupId> + <artifactId>arquillian-testenricher-resource</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + <artifactId>shrinkwrap-descriptors-depchain</artifactId> + <type>pom</type> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.jboss.weld.servlet</groupId> + <artifactId>weld-servlet</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-webapp</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-annotations</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.jboss.arquillian.container</groupId> + <artifactId>arquillian-jetty-embedded-9</artifactId> + <scope>test</scope> + </dependency> + +</dependencies> +---- + +You can see the tests in the `camel-example-cdi-rest-servlet` example +for a complete working example of testing a Camel CDI application using +the REST DSL and deployed as a WAR in Jetty. + +[[CDITesting-PAXExam]] +PAX Exam +^^^^^^^^ + +If you target OSGi as runtime environment for your Camel CDI +applications, you can use PAX Exam to automate the deployment of your +tests into an OSGi container, for example into Karaf, e.g.: + +[source,java] +---- +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerClass.class) +public class PaxCdiOsgiTest { + + @Configuration + public Option[] config() throws IOException { + return options( + // Karaf configuration + karafDistributionConfiguration() + .frameworkUrl( + maven() + .groupId("org.apache.karaf") + .artifactId("apache-karaf") + .versionAsInProject() + .type("zip")) + .name("Apache Karaf") + .unpackDirectory(new File("target/paxexam/unpack/")), + // PAX CDI Weld + features( + maven() + .groupId("org.ops4j.pax.cdi") + .artifactId("pax-cdi-features") + .type("xml") + .classifier("features") + .versionAsInProject(), + "pax-cdi-weld"), + // Karaf Camel commands + mavenBundle() + .groupId("your.application.groupId") + .artifactId("your.application.artifactId") + .versionAsInProject() + ); + } + + @Inject + private CamelContext context; + + @Test + public void testContextStatus() { + assertThat("Camel context status is incorrect!", + context.getStatus(), equalTo(ServiceStatus.Started)); + } +} +---- + +You can see the tests in the `camel-example-cdi-osgi` example for a +complete working example of testing a Camel CDI application deployed in +an OSGi container using PAX Exam. + +[[CDITesting-TestingPatterns]] +Testing Patterns +^^^^^^^^^^^^^^^^ + +You can see the tests in the `camel-example-cdi-test` example for a +thorough overview of the following testing patterns for Camel CDI +applications. + +[[CDITesting-Testroutes]] +Test routes ++++++++++++ + +You may want to add some Camel routes to your Camel CDI applications for +testing purpose. For example to route some exchanges to a `MockEndpoint` +instance. You can do that by declaring a `RouteBuilder` bean within the +test class as you would normally do in your application code, e.g.: + +[source,java] +---- +@RunWith(CamelCdiRunner.class) +public class CamelCdiTest { + + // Declare a RouteBuilder bean for testing purpose + // that is automatically added to the Camel context + static class TestRoute extends RouteBuilder { + + @Override + public void configure() { + from("direct:out").routeId("test").to("mock:out"); + } + + // And retrieve the MockEndpoint for further assertions + @Inject + @Uri("mock:out") + MockEndpoint mock; +} +---- + +You can find more information in <<cdi.adoc#CDI-Auto-detectingCamelroutes,auto-detecting Camel +routes>>. + +[[CDITesting-Beanalternatives]] +Bean alternatives ++++++++++++++++++ + +You may want to replace a bean that is used in your Camel routes by +another bean for testing purpose, for example to mock it or change the +behavior of the application bean. + +Imagine you have the following route in your application: + +[source,java] +---- +public class Application { + + @ContextName("camel-test-cdi") + static class Hello extends RouteBuilder { + + @Override + public void configure() { + from("direct:in").bean("bean").to("direct:out"); + } + } +} +---- + +And the corresponding bean: + +[source,java] +---- +@Named("bean") +public class Bean { + + public String process(@Body String body) { + return body; + } +} +---- + +Then you can replace the bean above in your tests by declaring an +_alternative_ bean, annotated with `@Alternative`, e.g.: + +[source,java] +---- +@Alternative +@Named("bean") +public class AlternativeBean { + + public String process(@Body String body) { + return body + " with alternative bean!"; + } +} +---- + +And you need to activate (a.k.a. _select_ in CDI terminology) this +alternative bean in your tests. If your using the `CamelCdiRunner` JUnit +runner, you can do that with the `@Beans` annotation provided by the +Camel CDI test module, e.g.: + +[source,java] +---- +@RunWith(CamelCdiRunner.class) +@Beans(alternatives = AlternativeBean.class) +public class CamelCdiTest { + + @Test + public void testAlternativeBean(@Uri("direct:in") ProducerTemplate producer + @Uri("mock:out") MockEndpoint mock) throws InterruptedException { + mock.expectedMessageCount(1); + mock.expectedBodiesReceived("test with alternative bean!"); + + producer.sendBody("test"); + + MockEndpoint.assertIsSatisfied(1L, TimeUnit.SECONDS, mock); + } + + static class TestRoute extends RouteBuilder { + + @Override + public void configure() { + from("direct:out").routeId("test").to("mock:out"); + } + } +} +---- + +[[CDITesting-Camelcontextcustomisation]] +Camel context customization ++++++++++++++++++++++++++++ + +You may need to customize your Camel contexts for testing purpose, for +example disabling JMX management to avoid TCP port allocation conflict. +You can do that by declaring a custom Camel context bean in your test +class, e.g.: + +[source,java] +---- +@RunWith(CamelCdiRunner.class) +public class CamelCdiTest { + + @Default + @ContextName("camel-test-cdi") + @ApplicationScoped + static class CustomCamelContext extends DefaultCamelContext { + + @PostConstruct + void customize() { + disableJMX(); + } + } +} +---- + +In that example, the custom Camel context bean declared in the test +class will be used during the test execution instead of the default +Camel context bean provided by the link:cdi.html[Camel CDI component]. + +[[CDITesting-RoutesadvisingwithadviceWith]] +Routes advising with `adviceWith` ++++++++++++++++++++++++++++++++++ + +`AdviceWith` is used for testing Camel routes where you +can _advice_ an existing route before its being tested. It allows to +add http://camel.apache.org/intercept.html[Intercept] or _weave_ routes +for testing purpose, for example using +the link:mock.html[Mock] component. + +It is recommended to only advice routes which are not started already. +To meet that requirement, you can use the `CamelContextStartingEvent` +event by declaring an observer method in which you use `adviceWith` to +add a `mock` endpoint at the end of your Camel route, e.g.: + +[source,java] +---- +@RunWith(CamelCdiRunner.class) +public class CamelCdiTest { + + void advice(@Observes CamelContextStartingEvent event, + @Uri("mock:test") MockEndpoint messages, + ModelCamelContext context) throws Exception { + + context.getRouteDefinition("route") + .adviceWith(context, new AdviceWithRouteBuilder() { + @Override + public void configure() { + weaveAddLast().to("mock:test"); + } + }); + } +} +---- + +[[CDITesting-JUnitrules]] +JUnit rules ++++++++++++ + +Camel CDI test starts the CDI container after all the JUnit class rules +have executed. + +That way, you can use JUnit class rules to initialize (resp. clean-up) +resources that your test classes would require during their execution +before the container initializes (resp. after the container has +shutdown). For example, you could use an embedded JMS broker +like https://activemq.apache.org/artemis/[ActiveMQ Artemis] to test your +Camel JMS application, e.g.: + +[source,java] +---- +import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS; + +@RunWith(CamelCdiRunner.class) +public class CamelCdiTest { + + @ClassRule + public static final ExternalResource resources = new ExternalResource() { + + private final EmbeddedJMS jms = new EmbeddedJMS(); + + @Override + protected void before() throws Exception { + jms.start(); + } + + @Override + protected void after() throws Exception { + jms.stop(); + } + }; + + @Inject + @Uri("jms:destination") + private ProducerTemplate producer; + + @Test + public void sendMessage() { + producer.sendBody("message"); + } +} +---- + +Another use case is to assert the behavior of your application after it +has shutdown. In that case, you can use the `Verifier` rule, e.g.: + +[source,java] +---- +import org.junit.rules.Verifier; + +@RunWith(CamelCdiRunner.class) +public class CamelCdiTest { + + @ClassRule + public static Verifier verifier = new Verifier() { + + @Override + protected void verify() { + // Executes after the CDI container has shutdown + } + }; +} +---- + +[[CDITesting-SeeAlso]] +See Also +^^^^^^^^ + +* link:cdi.html[CDI component] +* http://arquillian.org[Arquillian Web site] +* http://arquillian.org/modules/descriptors-shrinkwrap/[ShrinkWrap +Descriptors] +* http://arquillian.org/guides/shrinkwrap_introduction/[Creating +Deployable Archives with ShrinkWrap] +* https://ops4j1.jira.com/wiki/display/PAXEXAM4[PAX Exam] http://git-wip-us.apache.org/repos/asf/camel/blob/6abfa320/docs/user-manual/en/SUMMARY.md ---------------------------------------------------------------------- diff --git a/docs/user-manual/en/SUMMARY.md b/docs/user-manual/en/SUMMARY.md index f5336af..44b43fa 100644 --- a/docs/user-manual/en/SUMMARY.md +++ b/docs/user-manual/en/SUMMARY.md @@ -148,3 +148,5 @@ * User Guide * [Karaf](karaf.adoc) + * Testing + * [CDI Testing](cdi-testing.adoc)