I'm writing an application using camel for deployment (eventually) in a fuse container. The nature of the project requires that I mix and match Java and XML DSL.
I'm having trouble getting the mock framework to work properly with blueprint.
Here's my unit test, based completely on the example here.
public class MockNotWorking extends CamelBlueprintTestSupport {
#Test
public void testAdvisedMockEndpointsWithPattern() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
mockEndpoints("log*");
}
});
getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World");
getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
template.sendBody("direct:start", "Hello World");
// additional test to ensure correct endpoints in registry
assertNotNull(context.hasEndpoint("direct:start"));
assertNotNull(context.hasEndpoint("log:foo"));
assertNotNull(context.hasEndpoint("mock:result"));
// only the log:foo endpoint was mocked
assertNotNull(context.hasEndpoint("mock:log:foo"));
assertNull(context.hasEndpoint("mock:direct:start"));
assertNull(context.hasEndpoint("mock:direct:foo"));
assertMockEndpointsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start").to("direct:foo").to("log:foo").to("mock:result");
from("direct:foo").transform(constant("Bye World"));
}
};
}
protected String getBlueprintDescriptor() {
return "OSGI-INF/blueprint/blueprint.xml";
}
}
I have copied verbatim the example here, and modified it very slightly so we extend CamelBlueprintTestSupport instead of CamelTestSupport. This requires over-riding getBlueprintDescriptor to point to my blueprint xml, in which I have defined one very basic (and completely irrelevant to the test) route:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<camelContext id="validationRoute" xmlns="http://camel.apache.org/schema/blueprint" >
<route id="validation">
<from uri="direct:validation" />
<log message="validating..." />
</route>
</camelContext>
</blueprint>
The test fails with:
java.lang.AssertionError: mock://log:foo Received message count. Expected: <1> but was: <0>
So this means the message did not reach the mock endpoint. Change CamelBlueprintTestSupport for CamelTestSupport and it works.
So how do I get mocks like this working correctly with blueprint?
When you use blueprint, it imports all the routes you have defined in the blueprint xml file(s), and adds them to the CamelContext.
The reason this breaks things is that the context.getRouteDefinitions().get(0) no longer refers to the only route - there are now more than one. So when you add the AdviceWithRouteBuilder, it could be added to the wrong route.
The following code fixes the problem (and will work for non-blueprint tests too):
List<RouteDefinition> routes = context.getRouteDefinitions();
for (int i=0; i<routes.size(); i++) {
context.getRouteDefinitions().get(i).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mock all endpoints
mockEndpoints("log*");
}
});
}
======update======
A better way of doing this is rather than mocking all routes, find our specific route by id and then apply the advice. This means first setting the route id:
from("direct:start").routeId("start").to("direct:foo").to("log:foo").to("mock:result");
And then looking up the route by id and calling adviceWith as before:
context.getRouteDefinition("start").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mock log endpoints
mockEndpoints("log*");
}
});
Actually it depends on what version of Camel you are using, some methods doesn't work as expected. Its improved a lot better after 2.12 if I remember right. (2.15 is way better)
After all my encounters with Camel unit testing, I ended up documenting all stuffs here:
http://bushorn.com/unit-testing-apache-camel/
http://bushorn.com/camel-unit-testing-using-mock-endpoint/
By the way, instead of this "mockEndpoints("log*");", I would do this:
weaveByToString(".direct:foo.").after().to("mock:log"); (This sounds weird, I know ;) )
or if you can set an endpoint id
weaveById("endpoint-id-of-direct-foo").after().to("mock:log");
or
weaveAddLast().to("mock:log");
Related
I am new to apache camel so I am still struggling to write camel test cases.
I have below route defined
from("direct:routeToTest")
.id(ROUTE_ID)
.to(LOOK_UP_ROUTE)
.choice()
.when(some-condition)
.choice()
.when(condition)
.to(CREATE_ROUTE)
.otherwise().process(exchange -> exchange.getIn().setBody(prepareResponse(""))
.endChoice()
.otherwise()
.log("Some Issue")
.process(exchange -> unknownError(exchange))
.endChoice();
}
while testing I am trying to intercept to defined in my route and set some mock response to it. So after some search I found using adviceWith is the right way to achieve it.
So my test is like below. The outcome of the test is, it is still going to Look_up_route (direct:lookUpRoute, another route defined) for processing with the data passed but the expectation is code should skip this to and set response as "MockResponse"
#SpringBootTest
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
#RunWith(CamelSpringBootRunner.class)
#UseAdviceWith
#MockEndpoints
#DisableJmx(false)
public class RouteTest {
#Autowired
private ProducerTemplate producerTemplate;
#Autowired
private CamelContext context;
#Test
public void testResponseToJSON() throws Exception {
SomeObject someObject = getObject();
context.getRouteDefinition(ROUTE_ID).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
interceptSendToEndpoint(LOOK_UP_ROUTE)
.skipSendToOriginalEndpoint()
.transform("MockOutput");
}
);
context.start();
Object object = producerTemplate.requestBody(direct:routeToTest, someObject);
}
}
I'd like to know how do I skip .to(LOOK_UP_ROUTE) and set mockResponse to it.
Your intercept statement looks good at first sight. However, have a look at this answer for an alternative approach.
You have to
Add an id to the LOOK_UP_ROUTE step in your route
Then you can use adviceWith to remove or manipulate the marked step in your tests
Set the message body (and header) to whatever it should be for your test
I upgraded to camel 2.16 and one of my route Unit Tests started failing.
Here is my route definition:
public class Route extends RouteBuilder{
#Override
public void configure() throws Exception {
from(start).enrich("second");
from("direct:second")
.log(LoggingLevel.DEBUG, "foo", "Route [direct:second] started.");
}
}
Here is my test:
#RunWith(MockitoJUnitRunner.class)
public class RouteTest extends CamelTestSupport {
private Route builder;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Before
public void config() {
BasicConfigurator.configure();
}
#Override
protected RouteBuilder createRouteBuilder() {
builder = new Route();
return builder;
}
#Override
protected CamelContext createCamelContext() throws Exception {
SimpleRegistry registry = new SimpleRegistry();
return new DefaultCamelContext(registry);
}
#Test
public void testPrimeRouteForSubscriptionId() {
Exchange exchange = ExchangeBuilder.anExchange(new DefaultCamelContext()).build();
exchange.getIn().setBody(new String("test"));
template.send(exchange);
}
}
The error I'm getting when I run the test is:
org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://second]. Exchange[][Message: test]
Worthy of note is the following line in the camel 2.16 notes:
http://camel.apache.org/camel-2160-release.html
The resourceUri and resourceRef attributes on and has been removed as they now support a dynamic uris computed from an Expression.
Thanks in advance for any help.
Swap the order so the the direct route is started before the enrich.
http://camel.apache.org/configuring-route-startup-ordering-and-autostartup.html
Or use seda instead of direct in your unit test: http://camel.apache.org/seda
Or use ?block=true in the direct uri to tell Camel to block and wait for a consumer to be started and ready before it sends a message to it: http://camel.apache.org/direct
This is a somewhat old issue, but since i pulled out most of my hair out last night, trying to figure out why it was ok to use to("direct:myEndpoint") but not enrich("direct:myEndpoint"), I'll post the answer anyway - maybe it'll save somebody else from getting bald spots ;-)
It turns out to be a test-issue. In case of Direct endpoints, enrich checks whether there is a running route in the context before passing the Exchange to it, but it does so by looking at the CamelContext held by the Exchange it is currently handling. Since you passed your ProducerTemplate an Exchange what was created with a new DefaultCamelContext(), it has no "direct:second" route available.
Luckily there is a couple of simple solutions. Either create the Exchange using the CamelContext from CamelTestSupport, or use the ProducerTemplate sendBody(...) method instead:
#Test
public void testWithSendBody() {
template.sendBody(new String("test"));
}
#Test
public void testPrimeRouteForSubscriptionId() {
Exchange exchange = ExchangeBuilder.anExchange(context()).build();
exchange.getIn().setBody(new String("test"));
template.send(exchange);
}
The blueprint test keeps throwing exception, No Consumers available.
My scenario was that I have an osgi svc which exposes a method which can be called from any another osgi svc.
So the exposed svc method makes a call to a direct:
#EndpointInject(uri = "direct-vm:toRestCall")
ProducerTemplate toRestCall;
svcMethod(Exchange xch){
exchange.setOut(
toRestCall.send("seda:toDirectCall", xch -> {
try{
xch.getIn().setBody("abc");
}catch (Exception ex){
ex.getMessage();
}
}
}).getIn());
And when I tested the direct that it calls, Blueprint advice with JUnit used to keep throwing the following exception:
org.apache.camel.component.direct.DirectConsumerNotAvailableException:
No consumers available on endpoint: Endpoint. Exchange[Message: {..........
Here is Apache Camel route:
ZooKeeperRoutePolicy routePolicy = new ZooKeeperRoutePolicy("zookeeper:localhost:2181/fuse-example/routePolicy", 1);
from("file:camelInpit").routeId("systemARoute")
.routePolicy(routePolicy)
.log(LoggingLevel.ERROR, "Starting route")
[...]
I want to remove routePolicy in my tests since there is no ZooKeeper in test environment, but this is not as easy as it seems
context.getRouteDefinition("systemARoute").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:aaa");
weaveByType(RouteDefinition.class).selectIndex(1).remove();
}
});
weaveById("policy") and setting the id routePolicy(...).id("policy") does not help.
How can I dynamically remove RoutePolicies while testing?
Is it not possible to do something like that?
from("file:camelInpit").routeId("systemARoute")
.choice()
.when(prodEnvironmentExpression)
.routePolicy(routePolicy)
.endChoice()
.end()
.log(LoggingLevel.ERROR, "Starting route")
You can access the original route and set its route policies to null
context.getRouteDefinition("systemARoute").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
getOriginalRoute().setRoutePolicies(null);
}
});
But we should maybe add fluent DSL builders for this to make it stand out?
If you bind it to the context you can easily make a mock of the policy in your tests by using, where myPolicy is a mock or a policy that does nothing.
Even easier if you make an abstract MyCamelTestSupport which overides that and then all your tests which needs to mock it extends MyCamelTestSupport
#Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry jndi = super.createRegistry();
jndi.bind("myPolicy", myPolicy);
return jndi;
}
I have two programs, made up of three routes.
[
one is a route, from JPA database to bean.
one is a copier, from file system endpoint to file system endpoint
]
[
one is a uploader, from file system endpoint to bean.
]
I want to run the one program based on input from my property file
<context:property-placeholder location="./run.properties"
ignore-resource-not-found="false" />
But all I can find for content-based-routing, is examples where the choose is below the from. eg.
from("direct:start")
.choice()
.when(body().contains("Camel"))
.loadBalance().roundRobin().to("mock:foo").to("mock:bar")
.otherwise()
.to("mock:result");
I want a way to rearrange to something like this:
choice()
.when(body().contains("Camel"))
from("direct:start1").loadBalance().roundRobin().to("mock:foo").to("mock:bar")
.otherwise()
from("direct:start2").to("mock:result");
you don't need content based routing to control whether routes are started...
just use the autoStartup(boolean) API to control this...
for example...
from("activemq:queue:special").autoStartup("{{startupRouteProperty}}").to("file://backup");
see http://camel.apache.org/configuring-route-startup-ordering-and-autostartup.html
The way I ended up doing it:
static ClassPathXmlApplicationContext context;
static CamelContext camel;
...
context = new ClassPathXmlApplicationContext(
"META-INF/spring/camel-context.xml");
camel = new SpringCamelContext(context);
...
if (property) {
camel.addRoutes(new RouteBuilder() {
public void configure() {
from(..).to(..)
}
});
} else {
camel.addRoutes(new RouteBuilder() {
public void configure() {
from(..).to(..)
}
});
}
camel.start();
I'm trying to write some unit tests for a gwt-dispatch service with JUnit. I'm getting the following error when stepping through the test with my debugger:
Error in custom provider, com.google.inject.OutOfScopeException: Cannot access scoped object. Either we are not currently inside an HTTP Servlet request, or you may have forgotten to apply com.google.inject.servlet.GuiceFilter as a servlet filter for this request.
I'm going to simplify the code a bit here -- hopefully I'm not stripping out anything necessary.
import junit.framework.TestCase;
import net.customware.gwt.dispatch.client.standard.StandardDispatchService;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.ServletModule;
...
public class LoggedInServiceTest extends TestCase {
Injector i;
StandardDispatchService service;
protected com.google.inject.Injector getInjector() {
return Guice.createInjector(new ServletModule(),
new TestServletModule(),
new ActionsHandlerModule(),
new TestDispatchModule(),
new OpenIdGuiceModule());
}
public void setUp() throws Exception {
i = getInjector();
service = i.getInstance(StandardDispatchService.class);
}
public void testNotLoggedIn() {
try {
GetProjectsResult result = (GetProjectsResult) service.execute(new GetProjectsAction());
result.getSizeOfResult();
} catch (Exception e) {
fail();
}
}
}
The service request is indeed supposed to be going through a GuiceFilter, and it looks like that filter is not being set.
Any ideas on what other setup needs to be done to register the filter?
The problem is just what it states. You are trying to access a scoped object, but you are not currently in the scope. Most likely, your test is asking the injector for a RequestScoped object or an object that has a RequestScoped object in the injection dependency tree, but the test didn't do anything to enter the scope.
Binding the GuiceFilter in the test doesn't help, because your test isn't trying to send an HttpServletRequest through GuiceFilter to a servlet.
The best option would be to unit test your code. Create your classes in isolation, injecting mocks.
Assuming you want to do some kind of integration test, you have three options:
Have your test install a test module that called bindScope(RequestScoped.class, new FakeScope). The FakeScope class would implement Scope and have methods to enter and exit the scope. You may have to "seed" the scope with fake implementations of objects you depend on. See the Guice CustomScopes wiki page. This is the best option for integration tests, IMHO
Use ServletScopes.scopeRequest (Javadoc) to run part of the test code inside of a simulated request scope. This gets a bit ugly since you need to pass a Callable.
Do a full end-to-end test. Start your server and send it requests using Selenium. It's really hard to get good coverage this way, so I would leave this to things that you really need a browser to test.
Things might get a bit messy if the class you are testing depends indirectly on HttpServletRequest or HttpServletResponse. These classes can be challenging to setup correctly. Most of your classes should not depend on the servlet classes directly or indirectly. If that is not the case, you are either doing something wrong or you need to find a good action framework that allows you have most of your code not depend on these classes.
Here's an example of approach 1, using SimpleScope from the Guice CustomScopes wiki page:
public class LoggedInServiceTest extends TestCase {
private final Provider<StandardDispatchService> serviceProvider;
private final SimpleScope fakeRequestScope = new SimpleScope();
private final HttpServletRequest request = new FakeHttpServletRequest();
protected Injector createInjector() {
return Guice.createInjector(new FakeRequestScopeModule(),
new LoggedInServiceModule();
}
#Override
protected void setUp() throws Exception {
super.setUp();
Injector injector = createInjector();
scope.enter();
serviceProvider = injector.getProvider(StandardDispatchService.class);
}
#Override
protected void tearDown() throws Exception {
fakeRequestScope.exit()
super.tearDown();
}
public void testNotLoggedIn() {
fakeRequestScope.enter();
// fill in values of request
fakeRequestScope.seed(FakeHttpServletRequest.class, request);
StandardDispatchService service = serviceProvider.get();
GetProjectsAction action = new GetProjectsAction();
try {
service.execute(action);
fail();
} catch (NotLoggedInException expected) {
}
}
private class FakeRequestScopeModule extends AbstractModule() {
#Override
protected void configure() {
bind(RequestScoped.class, fakeRequestScope);
bind(HttpServletRequest.class)
.to(FakeHttpServletRequest.class)
.in(RequestScoped.class)
}
}
}
Write an AppSession interface and two implementations: HttpAppSession and MockAppSession.
Make your server-side handlers depend on AppSession and not on HttpSession directly.
Use Guice to inject HttpSession into HttpAppSession. That's the one you'll use in production, and for actually running your app. within a real servlet container.
The MockAppSession should not depend on HttpSession, nor HttpServletRequest, nor any other Guice Http scope. That's the one you'll use during testing.
Now, your Guice module should inject an AppSession implementation as follows:
bind(AppSession.class).to(MockAppSession.class)
bind(MockAppSession.class).in(Singleton.class)
That'll sort you out.