org.apache.camel.test.junit5.CamelTestSupport Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.test.junit5;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.camel.CamelContext;
import org.apache.camel.ConsumerTemplate;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.Expression;
import org.apache.camel.ExtendedCamelContext;
import org.apache.camel.FluentProducerTemplate;
import org.apache.camel.Message;
import org.apache.camel.NamedNode;
import org.apache.camel.NoSuchEndpointException;
import org.apache.camel.Predicate;
import org.apache.camel.Processor;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.RouteConfigurationsBuilder;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.Service;
import org.apache.camel.ServiceStatus;
import org.apache.camel.api.management.ManagedCamelContext;
import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
import org.apache.camel.builder.AdviceWith;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.InterceptSendToMockEndpointStrategy;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.debugger.DefaultDebugger;
import org.apache.camel.model.Model;
import org.apache.camel.model.ModelCamelContext;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.spi.CamelBeanPostProcessor;
import org.apache.camel.spi.Language;
import org.apache.camel.spi.PropertiesComponent;
import org.apache.camel.spi.PropertiesSource;
import org.apache.camel.spi.Registry;
import org.apache.camel.support.BreakpointSupport;
import org.apache.camel.support.EndpointHelper;
import org.apache.camel.test.CamelRouteCoverageDumper;
import org.apache.camel.util.StopWatch;
import org.apache.camel.util.StringHelper;
import org.apache.camel.util.TimeUtils;
import org.apache.camel.util.URISupport;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.camel.test.junit5.TestSupport.deleteDirectory;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* A useful base class which creates a {@link org.apache.camel.CamelContext} with some routes along with a
* {@link org.apache.camel.ProducerTemplate} for use in the test case Do not use this class for Spring Boot
* testing.
*/
public abstract class CamelTestSupport
implements BeforeEachCallback, AfterEachCallback, AfterAllCallback, BeforeAllCallback, BeforeTestExecutionCallback,
AfterTestExecutionCallback {
/**
* JVM system property which can be set to true to turn on dumping route coverage statistics.
*/
public static final String ROUTE_COVERAGE_ENABLED = "CamelTestRouteCoverage";
// CHECKSTYLE:OFF
private static final Logger LOG = LoggerFactory.getLogger(CamelTestSupport.class);
private static ThreadLocal threadCamelContext = new ThreadLocal<>();
private static ThreadLocal threadTemplate = new ThreadLocal<>();
private static ThreadLocal threadFluentTemplate = new ThreadLocal<>();
private static ThreadLocal threadConsumer = new ThreadLocal<>();
private static ThreadLocal threadService = new ThreadLocal<>();
protected Properties extra;
protected volatile ModelCamelContext context;
protected volatile ProducerTemplate template;
protected volatile FluentProducerTemplate fluentTemplate;
protected volatile ConsumerTemplate consumer;
protected volatile Service camelContextService;
@RegisterExtension
protected CamelTestSupport camelTestSupportExtension = this;
private boolean useRouteBuilder = true;
private final DebugBreakpoint breakpoint = new DebugBreakpoint();
private final StopWatch watch = new StopWatch();
private final Map fromEndpoints = new HashMap<>();
private static final ThreadLocal TESTS = new ThreadLocal<>();
private static final ThreadLocal INSTANCE = new ThreadLocal<>();
private String currentTestName;
private boolean isCreateCamelContextPerClass = false;
private CamelRouteCoverageDumper routeCoverageDumper = new CamelRouteCoverageDumper();
private ExtensionContext.Store globalStore;
private boolean testDirectoryCleaned;
// CHECKSTYLE:ON
@Override
public void afterTestExecution(ExtensionContext context) throws Exception {
watch.taken();
}
@Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
watch.restart();
}
public long timeTaken() {
return watch.taken();
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
currentTestName = context.getDisplayName();
globalStore = context.getStore(ExtensionContext.Namespace.GLOBAL);
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
DefaultCamelContext.clearOptions();
}
@Override
public void beforeAll(ExtensionContext context) {
isCreateCamelContextPerClass
= context.getTestInstanceLifecycle().filter(lc -> lc.equals(Lifecycle.PER_CLASS)).isPresent();
}
@Override
public void afterAll(ExtensionContext context) {
CamelTestSupport support = INSTANCE.get();
if (support != null && support.isCreateCamelContextPerClass()) {
try {
support.tearDownCreateCamelContextPerClass();
} catch (Exception e) {
// ignore
}
}
}
/**
* Use the RouteBuilder or not
*
* @return true then {@link CamelContext} will be auto started, false then {@link CamelContext}
* will not be auto started (you will have to start it manually)
*/
public boolean isUseRouteBuilder() {
return useRouteBuilder;
}
public void setUseRouteBuilder(boolean useRouteBuilder) {
this.useRouteBuilder = useRouteBuilder;
}
/**
* Whether to dump route coverage stats at the end of the test.
*
* This allows tooling or manual inspection of the stats, so you can generate a route trace diagram of which EIPs
* have been in use and which have not. Similar concepts as a code coverage report.
*
* You can also turn on route coverage globally via setting JVM system property
* CamelTestRouteCoverage=true.
*
* @return true to write route coverage status in an xml file in the target/camel-route-coverage
* directory after the test has finished.
*/
public boolean isDumpRouteCoverage() {
return false;
}
/**
* Override when using advice with and return true.
* This helps knowing advice with is to be used, and {@link CamelContext} will not be started before the advice with
* takes place. This helps by ensuring the advice with has been property setup before the {@link CamelContext} is
* started
*
* Important: Its important to start {@link CamelContext} manually from the unit test after you are done
* doing all the advice with.
*
* @return true if you use advice with in your unit tests.
*/
public boolean isUseAdviceWith() {
return false;
}
/**
* Tells whether {@link CamelContext} should be setup per test or per class.
*
* By default it will be setup/teardown per test method. This method returns true
when the camel test
* class is annotated with @TestInstance(TestInstance.Lifecycle.PER_CLASS).
*
* Important: Use this with care as the {@link CamelContext} will carry over state from previous tests, such
* as endpoints, components etc. So you cannot use this in all your tests.
*
* Setting up {@link CamelContext} uses the {@link #doPreSetup()}, {@link #doSetUp()}, and {@link #doPostSetup()}
* methods in that given order.
*
* @return true per class, false per test.
*/
public final boolean isCreateCamelContextPerClass() {
return isCreateCamelContextPerClass;
}
/**
* Override to enable auto mocking endpoints based on the pattern.
*
* Return * to mock all endpoints.
*
* @see EndpointHelper#matchEndpoint(CamelContext, String, String)
*/
public String isMockEndpoints() {
return null;
}
/**
* Override to enable auto mocking endpoints based on the pattern, and skip sending to original endpoint.
*
* Return * to mock all endpoints.
*
* @see EndpointHelper#matchEndpoint(CamelContext, String, String)
*/
public String isMockEndpointsAndSkip() {
return null;
}
public void replaceRouteFromWith(String routeId, String fromEndpoint) {
fromEndpoints.put(routeId, fromEndpoint);
}
/**
* Used for filtering routes matching the given pattern, which follows the following rules:
*
* - Match by route id - Match by route input endpoint uri
*
* The matching is using exact match, by wildcard and regular expression.
*
* For example to only include routes which starts with foo in their route id's, use: include=foo* And to
* exclude routes which starts from JMS endpoints, use: exclude=jms:*
*
* Multiple patterns can be separated by comma, for example to exclude both foo and bar routes, use:
* exclude=foo*,bar*
*
* Exclude takes precedence over include.
*/
public String getRouteFilterIncludePattern() {
return null;
}
/**
* Used for filtering routes matching the given pattern, which follows the following rules:
*
* - Match by route id - Match by route input endpoint uri
*
* The matching is using exact match, by wildcard and regular expression.
*
* For example to only include routes which starts with foo in their route id's, use: include=foo* And to
* exclude routes which starts from JMS endpoints, use: exclude=jms:*
*
* Multiple patterns can be separated by comma, for example to exclude both foo and bar routes, use:
* exclude=foo*,bar*
*
* Exclude takes precedence over include.
*/
public String getRouteFilterExcludePattern() {
return null;
}
/**
* Gets the name of the current test being executed.
*/
public final String getCurrentTestName() {
return currentTestName;
}
/**
* Override to enable debugger
*
* Is default false
*/
public boolean isUseDebugger() {
return false;
}
public Service getCamelContextService() {
return camelContextService;
}
public Service camelContextService() {
return camelContextService;
}
public CamelContext context() {
return context;
}
public ProducerTemplate template() {
return template;
}
public FluentProducerTemplate fluentTemplate() {
return fluentTemplate;
}
public ConsumerTemplate consumer() {
return consumer;
}
/**
* Allows a service to be registered a separate lifecycle service to start and stop the context; such as for Spring
* when the ApplicationContext is started and stopped, rather than directly stopping the CamelContext
*/
public void setCamelContextService(Service service) {
camelContextService = service;
threadService.set(camelContextService);
}
@BeforeEach
public void setUp() throws Exception {
LOG.info("********************************************************************************");
LOG.info("Testing: {} ({})", currentTestName, getClass().getName());
LOG.info("********************************************************************************");
if (isCreateCamelContextPerClass()) {
INSTANCE.set(this);
AtomicInteger v = TESTS.get();
if (v == null) {
v = new AtomicInteger();
TESTS.set(v);
}
if (v.getAndIncrement() == 0) {
LOG.debug("Setup CamelContext before running first test");
// test is per class, so only setup once (the first time)
doSpringBootCheck();
setupResources();
doPreSetup();
doSetUp();
doPostSetup();
} else {
LOG.debug("Reset between test methods");
// and in between tests we must do IoC and reset mocks
postProcessTest();
resetMocks();
}
} else {
// test is per test so always setup
doSpringBootCheck();
setupResources();
doPreSetup();
doSetUp();
doPostSetup();
}
// only start timing after all the setup
watch.restart();
}
/**
* Strategy to perform any pre setup, before {@link CamelContext} is created
*/
protected void doPreSetup() throws Exception {
// noop
}
/**
* Strategy to perform any post setup after {@link CamelContext} is created
*/
protected void doPostSetup() throws Exception {
// noop
}
/**
* Detects if this is a Spring-Boot test and throws an exception, as these base classes is not intended for testing
* Camel on Spring Boot.
*/
protected void doSpringBootCheck() {
boolean springBoot = hasClassAnnotation("org.springframework.boot.test.context.SpringBootTest");
if (springBoot) {
throw new RuntimeException(
"Spring Boot detected: The CamelTestSupport/CamelSpringTestSupport class is not intended for Camel testing with Spring Boot.");
}
}
private void doSetUp() throws Exception {
LOG.debug("setUp test");
// jmx is enabled if we have configured to use it, or if dump route
// coverage is enabled (it requires JMX)
boolean jmx = useJmx() || isRouteCoverageEnabled();
if (jmx) {
enableJMX();
} else {
disableJMX();
}
context = (ModelCamelContext) createCamelContext();
threadCamelContext.set(context);
assertNotNull(context, "No context found!");
// add custom beans
bindToRegistry(context.getRegistry());
// reduce default shutdown timeout to avoid waiting for 300 seconds
context.getShutdownStrategy().setTimeout(getShutdownTimeout());
// set debugger if enabled
if (isUseDebugger()) {
if (context.getStatus().equals(ServiceStatus.Started)) {
LOG.info("Cannot setting the Debugger to the starting CamelContext, stop the CamelContext now.");
// we need to stop the context first to setup the debugger
context.stop();
}
context.setDebugging(true);
context.setDebugger(new DefaultDebugger());
context.getDebugger().addBreakpoint(breakpoint);
// when stopping CamelContext it will automatically remove the breakpoint
}
template = context.createProducerTemplate();
template.start();
fluentTemplate = context.createFluentProducerTemplate();
fluentTemplate.start();
consumer = context.createConsumerTemplate();
consumer.start();
threadTemplate.set(template);
threadFluentTemplate.set(fluentTemplate);
threadConsumer.set(consumer);
// enable auto mocking if enabled
String pattern = isMockEndpoints();
if (pattern != null) {
context.adapt(ExtendedCamelContext.class)
.registerEndpointCallback(new InterceptSendToMockEndpointStrategy(pattern));
}
pattern = isMockEndpointsAndSkip();
if (pattern != null) {
context.adapt(ExtendedCamelContext.class)
.registerEndpointCallback(new InterceptSendToMockEndpointStrategy(pattern, true));
}
// configure properties component (mandatory for testing)
PropertiesComponent pc = context.getPropertiesComponent();
if (extra == null) {
extra = useOverridePropertiesWithPropertiesComponent();
}
if (extra != null && !extra.isEmpty()) {
pc.setOverrideProperties(extra);
}
pc.addPropertiesSource(new PropertiesSource() {
@Override
public String getName() {
return "junit-store";
}
@Override
public String getProperty(String name) {
return globalStore.get(name, String.class);
}
});
Boolean ignore = ignoreMissingLocationWithPropertiesComponent();
if (ignore != null) {
pc.setIgnoreMissingLocation(ignore);
}
String include = getRouteFilterIncludePattern();
String exclude = getRouteFilterExcludePattern();
if (include != null || exclude != null) {
LOG.info("Route filtering pattern: include={}, exclude={}", include, exclude);
context.getExtension(Model.class).setRouteFilterPattern(include, exclude);
}
// prepare for in-between tests
postProcessTest();
if (isUseRouteBuilder()) {
RoutesBuilder[] builders = createRouteBuilders();
// add configuration before routes
for (RoutesBuilder builder : builders) {
if (builder instanceof RouteConfigurationsBuilder) {
LOG.debug("Using created route configuration: {}", builder);
context.addRoutesConfigurations((RouteConfigurationsBuilder) builder);
}
}
for (RoutesBuilder builder : builders) {
LOG.debug("Using created route builder: {}", builder);
context.addRoutes(builder);
}
replaceFromEndpoints();
boolean skip = "true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext"));
if (skip) {
LOG.info("Skipping starting CamelContext as system property skipStartingCamelContext is set to be true.");
} else if (isUseAdviceWith()) {
LOG.info("Skipping starting CamelContext as isUseAdviceWith is set to true.");
} else {
startCamelContext();
}
} else {
replaceFromEndpoints();
LOG.debug("Using route builder from the created context: {}", context);
}
LOG.debug("Routing Rules are: {}", context.getRoutes());
assertValidContext(context);
}
private void replaceFromEndpoints() throws Exception {
for (final Map.Entry entry : fromEndpoints.entrySet()) {
AdviceWith.adviceWith(context.getRouteDefinition(entry.getKey()), context, new AdviceWithRouteBuilder() {
@Override
public void configure() throws Exception {
replaceFromWith(entry.getValue());
}
});
}
}
private boolean isRouteCoverageEnabled() {
return System.getProperty(ROUTE_COVERAGE_ENABLED, "false").equalsIgnoreCase("true") || isDumpRouteCoverage();
}
@AfterEach
public void tearDown() throws Exception {
long time = watch.taken();
LOG.info("********************************************************************************");
LOG.info("Testing done: {} ({})", currentTestName, getClass().getName());
LOG.info("Took: {} ({} millis)", TimeUtils.printDuration(time), time);
// if we should dump route stats, then write that to a file
if (isRouteCoverageEnabled()) {
String className = this.getClass().getSimpleName();
String dir = "target/camel-route-coverage";
String name = className + "-" + StringHelper.before(currentTestName, "(") + ".xml";
ManagedCamelContext mc = context != null ? context.getExtension(ManagedCamelContext.class) : null;
ManagedCamelContextMBean managedCamelContext = mc != null ? mc.getManagedCamelContext() : null;
if (managedCamelContext == null) {
LOG.warn("Cannot dump route coverage to file as JMX is not enabled. "
+ "Add camel-management JAR as dependency and/or override useJmx() method to enable JMX in the unit test classes.");
} else {
routeCoverageDumper.dump(managedCamelContext, context, dir, name, getClass().getName(), currentTestName,
timeTaken());
}
}
LOG.info("********************************************************************************");
if (isCreateCamelContextPerClass()) {
// will tear down test specially in afterAll callback
} else {
LOG.debug("tearDown()");
doStopTemplates(consumer, template, fluentTemplate);
doStopCamelContext(context, camelContextService);
doPostTearDown();
cleanupResources();
}
testDirectoryCleaned = false;
}
void tearDownCreateCamelContextPerClass() throws Exception {
LOG.debug("tearDownCreateCamelContextPerClass()");
TESTS.remove();
doStopTemplates(threadConsumer.get(), threadTemplate.get(), threadFluentTemplate.get());
doStopCamelContext(threadCamelContext.get(), threadService.get());
doPostTearDown();
cleanupResources();
}
/**
* Strategy to perform any post action, after {@link CamelContext} is stopped
*/
protected void doPostTearDown() throws Exception {
// noop
}
/**
* Strategy to perform resources setup, before {@link CamelContext} is created
*/
protected void setupResources() throws Exception {
deleteTestDirectory();
}
/**
* Strategy to perform resources cleanup, after {@link CamelContext} is stopped
*/
protected void cleanupResources() throws Exception {
// noop
}
/**
* Returns the timeout to use when shutting down (unit in seconds).
*
* Will default use 10 seconds.
*
* @return the timeout to use
*/
protected int getShutdownTimeout() {
return 10;
}
/**
* Whether or not JMX should be used during testing.
*
* @return false by default.
*/
protected boolean useJmx() {
return false;
}
/**
* Override this method to include and override properties with the Camel {@link PropertiesComponent}.
*
* @return additional properties to add/override.
*/
protected Properties useOverridePropertiesWithPropertiesComponent() {
return null;
}
/**
* Whether to ignore missing locations with the {@link PropertiesComponent}. For example when unit testing you may
* want to ignore locations that are not available in the environment used for testing.
*
* @return true to ignore, false to not ignore, and null to leave as configured on the
* {@link PropertiesComponent}
*/
protected Boolean ignoreMissingLocationWithPropertiesComponent() {
return null;
}
protected void postProcessTest() throws Exception {
context = threadCamelContext.get();
template = threadTemplate.get();
fluentTemplate = threadFluentTemplate.get();
consumer = threadConsumer.get();
camelContextService = threadService.get();
applyCamelPostProcessor();
}
/**
* Applies the {@link CamelBeanPostProcessor} to this instance.
*
* Derived classes using IoC / DI frameworks may wish to turn this into a NoOp such as for CDI we would just use CDI
* to inject this
*/
protected void applyCamelPostProcessor() throws Exception {
// use the bean post processor if the test class is not dependency
// injected already by Spring Framework
boolean spring = hasClassAnnotation("org.springframework.boot.test.context.SpringBootTest",
"org.springframework.context.annotation.ComponentScan");
if (!spring) {
context.getExtension(ExtendedCamelContext.class).getBeanPostProcessor().postProcessBeforeInitialization(this,
getClass().getName());
context.getExtension(ExtendedCamelContext.class).getBeanPostProcessor().postProcessAfterInitialization(this,
getClass().getName());
}
}
/**
* Does this test class have any of the following annotations on the class-level.
*/
protected boolean hasClassAnnotation(String... names) {
for (String name : names) {
for (Annotation ann : getClass().getAnnotations()) {
String annName = ann.annotationType().getName();
if (annName.equals(name)) {
return true;
}
}
}
return false;
}
protected void stopCamelContext() throws Exception {
doStopCamelContext(context, camelContextService);
}
private static void doStopCamelContext(CamelContext context, Service camelContextService) {
if (camelContextService != null) {
if (camelContextService == threadService.get()) {
threadService.remove();
}
camelContextService.stop();
} else {
if (context != null) {
if (context == threadCamelContext.get()) {
threadCamelContext.remove();
}
context.stop();
}
}
}
private static void doStopTemplates(
ConsumerTemplate consumer, ProducerTemplate template, FluentProducerTemplate fluentTemplate) {
if (consumer != null) {
if (consumer == threadConsumer.get()) {
threadConsumer.remove();
}
consumer.stop();
}
if (template != null) {
if (template == threadTemplate.get()) {
threadTemplate.remove();
}
template.stop();
}
if (fluentTemplate != null) {
if (fluentTemplate == threadFluentTemplate.get()) {
threadFluentTemplate.remove();
}
fluentTemplate.stop();
}
}
protected void startCamelContext() throws Exception {
if (camelContextService != null) {
camelContextService.start();
} else {
if (context instanceof DefaultCamelContext) {
DefaultCamelContext defaultCamelContext = (DefaultCamelContext) context;
if (!defaultCamelContext.isStarted()) {
defaultCamelContext.start();
}
} else {
context.start();
}
}
}
protected CamelContext createCamelContext() throws Exception {
Registry registry = createCamelRegistry();
CamelContext retContext;
if (registry != null) {
retContext = new DefaultCamelContext(registry);
} else {
retContext = new DefaultCamelContext();
}
return retContext;
}
/**
* Allows to bind custom beans to the Camel {@link Registry}.
*/
protected void bindToRegistry(Registry registry) throws Exception {
// noop
}
/**
* Override to use a custom {@link Registry}.
*
* However if you need to bind beans to the registry then this is possible already with the bind method on registry,
* and there is no need to override this method.
*/
protected Registry createCamelRegistry() throws Exception {
return null;
}
/**
* Factory method which derived classes can use to create a {@link RouteBuilder} to define the routes for testing
*/
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@Override
public void configure() {
// no routes added by default
}
};
}
/**
* Factory method which derived classes can use to create an array of {@link org.apache.camel.builder.RouteBuilder}s
* to define the routes for testing
*
* @see #createRouteBuilder()
*/
protected RoutesBuilder[] createRouteBuilders() throws Exception {
return new RoutesBuilder[] { createRouteBuilder() };
}
/**
* Resolves a mandatory endpoint for the given URI or an exception is thrown
*
* @param uri the Camel URI to use to create or resolve an endpoint
* @return the endpoint
*/
protected Endpoint resolveMandatoryEndpoint(String uri) {
return TestSupport.resolveMandatoryEndpoint(context, uri);
}
/**
* Resolves a mandatory endpoint for the given URI and expected type or an exception is thrown
*
* @param uri the Camel URI to use to create or resolve an endpoint
* @return the endpoint
*/
protected T resolveMandatoryEndpoint(String uri, Class endpointType) {
return TestSupport.resolveMandatoryEndpoint(context, uri, endpointType);
}
/**
* Resolves the mandatory Mock endpoint using a URI of the form mock:someName
*
* @param uri the URI which typically starts with "mock:" and has some name
* @return the mandatory mock endpoint or an exception is thrown if it could not be resolved
*/
protected MockEndpoint getMockEndpoint(String uri) {
return getMockEndpoint(uri, true);
}
/**
* Resolves the {@link MockEndpoint} using a URI of the form mock:someName
, optionally creating it if
* it does not exist. This implementation will lookup existing mock endpoints and match on the mock queue name, eg
* mock:foo and mock:foo?retainFirst=5 would match as the queue name is foo.
*
* @param uri the URI which typically starts with "mock:" and has some name
* @param create whether or not to allow the endpoint to be created if it doesn't exist
* @return the mock endpoint or an {@link NoSuchEndpointException} is thrown if it could not
* be resolved
* @throws NoSuchEndpointException is the mock endpoint does not exists
*/
protected MockEndpoint getMockEndpoint(String uri, boolean create) throws NoSuchEndpointException {
// look for existing mock endpoints that have the same queue name, and
// to
// do that we need to normalize uri and strip out query parameters and
// whatnot
String n;
try {
n = URISupport.normalizeUri(uri);
} catch (Exception e) {
throw RuntimeCamelException.wrapRuntimeException(e);
}
// strip query
int idx = n.indexOf('?');
if (idx != -1) {
n = n.substring(0, idx);
}
final String target = n;
// lookup endpoints in registry and try to find it
MockEndpoint found = (MockEndpoint) context.getEndpointRegistry().values().stream()
.filter(e -> e instanceof MockEndpoint).filter(e -> {
String t = e.getEndpointUri();
// strip query
int idx2 = t.indexOf('?');
if (idx2 != -1) {
t = t.substring(0, idx2);
}
return t.equals(target);
}).findFirst().orElse(null);
if (found != null) {
return found;
}
if (create) {
return resolveMandatoryEndpoint(uri, MockEndpoint.class);
} else {
throw new NoSuchEndpointException(String.format("MockEndpoint %s does not exist.", uri));
}
}
/**
* Sends a message to the given endpoint URI with the body value
*
* @param endpointUri the URI of the endpoint to send to
* @param body the body for the message
*/
protected void sendBody(String endpointUri, final Object body) {
template.send(endpointUri, exchange -> {
Message in = exchange.getIn();
in.setBody(body);
});
}
/**
* Sends a message to the given endpoint URI with the body value and specified headers
*
* @param endpointUri the URI of the endpoint to send to
* @param body the body for the message
* @param headers any headers to set on the message
*/
protected void sendBody(String endpointUri, final Object body, final Map headers) {
template.send(endpointUri, exchange -> {
Message in = exchange.getIn();
in.setBody(body);
for (Map.Entry entry : headers.entrySet()) {
in.setHeader(entry.getKey(), entry.getValue());
}
});
}
/**
* Sends messages to the given endpoint for each of the specified bodies
*
* @param endpointUri the endpoint URI to send to
* @param bodies the bodies to send, one per message
*/
protected void sendBodies(String endpointUri, Object... bodies) {
for (Object body : bodies) {
sendBody(endpointUri, body);
}
}
/**
* Creates an exchange with the given body
*/
protected Exchange createExchangeWithBody(Object body) {
return TestSupport.createExchangeWithBody(context, body);
}
/**
* Asserts that the given language name and expression evaluates to the given value on a specific exchange
*/
protected void assertExpression(Exchange exchange, String languageName, String expressionText, Object expectedValue) {
Language language = assertResolveLanguage(languageName);
Expression expression = language.createExpression(expressionText);
assertNotNull(expression, "No Expression could be created for text: " + expressionText + " language: " + language);
TestSupport.assertExpression(expression, exchange, expectedValue);
}
/**
* Asserts that the given language name and predicate expression evaluates to the expected value on the message
* exchange
*/
protected void assertPredicate(String languageName, String expressionText, Exchange exchange, boolean expected) {
Language language = assertResolveLanguage(languageName);
Predicate predicate = language.createPredicate(expressionText);
assertNotNull(predicate, "No Predicate could be created for text: " + expressionText + " language: " + language);
TestSupport.assertPredicate(predicate, exchange, expected);
}
/**
* Asserts that the language name can be resolved
*/
protected Language assertResolveLanguage(String languageName) {
Language language = context.resolveLanguage(languageName);
assertNotNull(language, "No language found for name: " + languageName);
return language;
}
/**
* Asserts that all the expectations of the Mock endpoints are valid
*/
protected void assertMockEndpointsSatisfied() throws InterruptedException {
MockEndpoint.assertIsSatisfied(context);
}
/**
* Asserts that all the expectations of the Mock endpoints are valid
*/
protected void assertMockEndpointsSatisfied(long timeout, TimeUnit unit) throws InterruptedException {
MockEndpoint.assertIsSatisfied(context, timeout, unit);
}
/**
* Reset all Mock endpoints.
*/
protected void resetMocks() {
MockEndpoint.resetMocks(context);
}
protected void assertValidContext(CamelContext context) {
assertNotNull(context, "No context found!");
}
protected T getMandatoryEndpoint(String uri, Class type) {
T endpoint = context.getEndpoint(uri, type);
assertNotNull(endpoint, "No endpoint found for uri: " + uri);
return endpoint;
}
protected Endpoint getMandatoryEndpoint(String uri) {
Endpoint endpoint = context.getEndpoint(uri);
assertNotNull(endpoint, "No endpoint found for uri: " + uri);
return endpoint;
}
/**
* Disables the JMX agent. Must be called before the {@link #setUp()} method.
*/
protected void disableJMX() {
DefaultCamelContext.setDisableJmx(true);
}
/**
* Enables the JMX agent. Must be called before the {@link #setUp()} method.
*/
protected void enableJMX() {
DefaultCamelContext.setDisableJmx(false);
}
/**
* Single step debugs and Camel invokes this method before entering the given processor
*/
protected void debugBefore(
Exchange exchange, Processor processor, ProcessorDefinition> definition, String id, String label) {
}
/**
* Single step debugs and Camel invokes this method after processing the given processor
*/
protected void debugAfter(
Exchange exchange, Processor processor, ProcessorDefinition> definition, String id, String label,
long timeTaken) {
}
/**
* To easily debug by overriding the debugBefore and debugAfter methods.
*/
private class DebugBreakpoint extends BreakpointSupport {
@Override
public void beforeProcess(Exchange exchange, Processor processor, NamedNode definition) {
CamelTestSupport.this.debugBefore(exchange, processor, (ProcessorDefinition>) definition, definition.getId(),
definition.getLabel());
}
@Override
public void afterProcess(Exchange exchange, Processor processor, NamedNode definition, long timeTaken) {
CamelTestSupport.this.debugAfter(exchange, processor, (ProcessorDefinition>) definition, definition.getId(),
definition.getLabel(), timeTaken);
}
}
protected Path testDirectory() {
return testDirectory(false);
}
protected Path testDirectory(boolean create) {
Class> testClass = getClass();
return testDirectory(testClass, create);
}
public static Path testDirectory(Class> testClass, boolean create) {
Path dir = Paths.get("target", "data", testClass.getSimpleName());
if (create) {
try {
Files.createDirectories(dir);
} catch (IOException e) {
throw new IllegalStateException("Unable to create test directory: " + dir, e);
}
}
return dir;
}
protected Path testFile(String dir) {
return testDirectory().resolve(dir);
}
protected Path testDirectory(String dir) {
return testDirectory(dir, false);
}
protected Path testDirectory(String dir, boolean create) {
Path f = testDirectory().resolve(dir);
if (create) {
try {
Files.createDirectories(f);
} catch (IOException e) {
throw new IllegalStateException("Unable to create test directory: " + dir, e);
}
}
return f;
}
protected String fileUri() {
return "file:" + testDirectory();
}
protected String fileUri(String query) {
return "file:" + testDirectory() + (query.startsWith("?") ? "" : "/") + query;
}
public void deleteTestDirectory() {
if (!testDirectoryCleaned) {
deleteDirectory(testDirectory());
testDirectoryCleaned = true;
}
}
}