Is it possible to use adviceWith when using Spring/Camel's annotation support? I was originally trying to use this method, but had to extend CamelTestSupport and its context did not have my routes in it.
On Jun 11, 2014, at 11:39 PM, Claus Ibsen <[email protected]> wrote: > Hi > > You may want to use @MockEndpointsAndSkip so you do not call the SQL > component. > > The camel-spring-test with the annotations was added to Camel later, > after the book was published. > > But you can find the annotations and more details here > http://camel.apache.org/spring-testing.html > > But as the SQL endpoint is dynamic calculated then its easier to use > interceptSendToEndpoint and skip, as shown in the book on page 182 > with the advice with. > > > > > On Thu, Jun 12, 2014 at 1:58 AM, Matt Raible <[email protected]> wrote: >> Nope, my routes are defined using the Java DSL, not XML. Changing from: >> >> @ContextConfiguration(classes = CamelConfig.class) >> >> To: >> >> @ContextConfiguration(loader = CamelSpringDelegatingTestContextLoader.class, >> classes = CamelConfig.class) >> >> Solved my problem. >> >> I don't know if the regex needs to change. Using @MockEndpoints("sql:*"), I >> see the following in my logs: >> >> 2014-06-11 17:53:35,388 [main ] INFO output >> - Exchange[ExchangePattern: InOnly, BodyType: java.util.ArrayList, Body: >> []] >> 2014-06-11 17:53:35,389 [main ] INFO MockEndpoint >> - Asserting: Endpoint[mock://sql:select...] is satisfied >> 2014-06-11 17:53:35,390 [main ] INFO MockEndpoint >> - Asserting: Endpoint[mock://sql:*] is satisfied >> >> It looks like it's working, but the test is failing: >> >> java.lang.AssertionError: mock://sql:* Received message count. Expected: <1> >> but was: <0> >> >> So now I want to do two things: 1) understand why my mockSQL endpoint is not >> receiving a message and 2) make the mock SQL endpoint return an ArrayList of >> items so I can test my processing logic. >> >> On Jun 11, 2014, at 5:46 PM, Minh Tran <[email protected]> wrote: >> >>> It appears to me like you have your routes defined in xml and not actually >>> in JavaConfig? In that case, you can simplify your configuration even >>> further and not refer to your JavaConfig class like this >>> >>> @RunWith(CamelSpringJUnit4ClassRunner.class) >>> @ContextConfiguration(loader = >>> CamelSpringDelegatingTestContextLoader.class, locations = { >>> "classpath:/path/to/xml" }) >>> @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) >>> @MockEndpointsAndSkip("sql:.*") >>> public class FooRouteTests >>> >>> No need to extend any class. >>> Also your regex has to be "sql:.*" and not "sql:*" They mean two different >>> things in regex. >>> >>> On 12/06/2014, at 9:32 AM, Matt Raible <[email protected]> wrote: >>> >>>> Thanks for your advice. Here's my attempt to modify my test to use >>>> CamelSpringJUnit4ClassRunner and annotations to mock my SQL endpoint. >>>> >>>> @RunWith(CamelSpringJUnit4ClassRunner.class) >>>> @ContextConfiguration(classes = CamelConfig.class) >>>> @DirtiesContext(classMode = >>>> DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) >>>> @MockEndpoints("sql:*") >>>> public class FooRouteTests { >>>> >>>> @Autowired >>>> CamelContext camelContext; >>>> >>>> @Produce >>>> ProducerTemplate template; >>>> >>>> @EndpointInject(uri = "mock:sql:*") >>>> MockEndpoint mockSql; >>>> >>>> @Test >>>> public void testMockSQLEndpoint() throws Exception { >>>> template.sendBody("direct:foo", "bar"); >>>> >>>> mockSql.expectedMessageCount(1); >>>> // todo: take input message and return mock results >>>> (ArrayList<HashMap>) >>>> MockEndpoint.assertIsSatisfied(camelContext); >>>> } >>>> } >>>> >>>> For some reason, this results in an error, even though my CamelConfig >>>> works for configuring other tests. >>>> >>>> Could not autowire field: org.apache.camel.CamelContext >>>> com.company.app.foo.FooRouteTests.camelContext; nested exception is >>>> org.springframework.beans.factory.NoSuchBeanDefinitionException: No >>>> qualifying bean of type [org.apache.camel.CamelContext] found for >>>> dependency: expected at least 1 bean which qualifies as autowire candidate >>>> for this dependency. >>>> >>>> @Configuration >>>> @ImportResource("classpath:META-INF/cxf/cxf.xml") >>>> @ComponentScan("com.company.app") >>>> public class CamelConfig extends CamelConfiguration { >>>> >>>> @Override >>>> protected void setupCamelContext(CamelContext camelContext) throws >>>> Exception { >>>> PropertiesComponent pc = new PropertiesComponent(); >>>> pc.setLocation("classpath:application.properties"); >>>> camelContext.addComponent("properties", pc); >>>> super.setupCamelContext(camelContext); >>>> } >>>> } >>>> >>>> >>>> On Jun 11, 2014, at 5:08 PM, Minh Tran <[email protected]> wrote: >>>> >>>>> If you're using Spring, I recommend not extending any of the Camel Test >>>>> classes and using the Camel Enhanced Spring Test as described here >>>>> http://camel.apache.org/spring-testing.html >>>>> >>>>> The docs take a bit of getting use to because it describes several >>>>> different ways of testing via Spring but you just have to skip to the >>>>> Camel Enhanced Spring Test bits. It also doesn't describe how to test >>>>> using a JavaConfig class very well IMO. It only describes how to do this >>>>> by extending AbstractJUnit4SpringContextTests which is a really old way >>>>> of doing spring unit tests. I had to do a lot of experimenting to get it >>>>> to work without extending this class. >>>>> >>>>> Here's an example I had, the only difference is my JavaConfig is embedded >>>>> into my unit test class, but there's no reason you couldn't refer to an >>>>> existing class. If you want to mock and skip your sql or soap calls, then >>>>> instead of using @MockEndPoints, use @MockEndPointsAndSkip. Look further >>>>> down to see some gotchas that I encountered in all of this. >>>>> >>>>> >>>>> @RunWith(CamelSpringJUnit4ClassRunner.class) >>>>> @ContextConfiguration(loader = >>>>> CamelSpringDelegatingTestContextLoader.class, classes = >>>>> RegexTest.JavaConfig.class) >>>>> @MockEndpoints >>>>> @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) >>>>> public class RegexTest { >>>>> >>>>> @Produce(uri = "direct:start") >>>>> private ProducerTemplate producerTemplate; >>>>> >>>>> @EndpointInject(uri = "mock:direct:match") >>>>> private MockEndpoint matchEndpoint; >>>>> >>>>> @EndpointInject(uri = "mock:direct:nomatch") >>>>> private MockEndpoint noMatchEndpoint; >>>>> >>>>> @Configuration >>>>> public static class JavaConfig extends SingleRouteCamelConfiguration { >>>>> >>>>> @Override >>>>> public RouteBuilder route() { >>>>> return new RouteBuilder() { >>>>> >>>>> @Override >>>>> public void configure() throws Exception { >>>>> >>>>> from("direct:start").to("log:blah?showProperties=true").log("${property.scaleResponse.message}").choice().when() >>>>> >>>>> .simple("resource:classpath:simple/item_not_exists.txt").to("direct:match").otherwise().to("direct:nomatch").end(); >>>>> from("direct:match").log("matched"); >>>>> from("direct:nomatch").log("no match"); >>>>> this.getContext().setTracing(true); >>>>> } >>>>> }; >>>>> } >>>>> } >>>>> >>>>> @After >>>>> public void afterTest() throws InterruptedException { >>>>> matchEndpoint.assertIsSatisfied(); >>>>> noMatchEndpoint.assertIsSatisfied(); >>>>> } >>>>> >>>>> @Test >>>>> public void testMatch() { >>>>> InterfaceResponse response = new InterfaceResponse(); >>>>> response.setMessage("ITEM XML Download ended. : Item \"blah\" >>>>> does not exist. - "); >>>>> matchEndpoint.expectedMessageCount(1); >>>>> >>>>> producerTemplate.sendBodyAndProperty(null, "scaleResponse", >>>>> response); >>>>> >>>>> } >>>>> >>>>> } >>>>> >>>>> >>>>> The regex you provide to mockendpointandskip and mock endpoint is >>>>> important to get right. I didn't add any regex to my example above >>>>> because mocking all endpoints (the default) was ok in my example. if you >>>>> get this regex wrong, camel doesn't warn you. You can turn on camel >>>>> logging to see whether it has mocked your endpoint correctly or not. It >>>>> should say something like the following. That's how you know it is >>>>> working. >>>>> >>>>> INFO org.apache.camel.impl.InterceptSendToMockEndpointStrategy - Adviced >>>>> endpoint [direct://start] with mock endpoint [mock:direct:start] >>>>> >>>>> The regex value matching is a bit strange, if it doesn't match your >>>>> endpoint even though you are absolutely sure it is correct, try tacking >>>>> on ".*" on the end of it, this fixed it up for me many times. IMO I think >>>>> it's a bug in the camel regex matching somewhere. >>>>> >>>>> When you do the @EndpointInject uri, make sure you prepend with "mock" >>>>> and don't include anything pass the "?" in your uri. This wasn't obvious >>>>> to me. And again camel won't warn you if you get this wrong. >>>>> >>>>> @DirtiesContext is a must otherwise you get strange behaviour once one >>>>> test starts failing. >>>>> >>>>> Hope that helps. >>>>> >>>>> On 12/06/2014, at 8:27 AM, Matt Raible <[email protected]> wrote: >>>>> >>>>>> Thanks for the advice. I bought the book, read chapter 6 and I'm trying >>>>>> to use the advice builder. Chapter 6 talks about using mocks quite a >>>>>> bit, which seems useful in building a route, but not when it's already >>>>>> built. >>>>>> >>>>>> My routes are configured with Spring and JavaConfig in a CamelConfig >>>>>> class. When I try to use CamelTestSupport as my parent class, the >>>>>> context doesn't have any route definitions in it. In other words, >>>>>> context.getRouteDefinitions() returns an empty list. How do I get >>>>>> CamelTestSupport to recognize my routes configured in Spring? Or is it >>>>>> possible to inject the context and template and use adviceWith w/o >>>>>> extending CamelTestSupport? >>>>>> >>>>>> Thanks, >>>>>> >>>>>> Matt >>>>>> >>>>>> @RunWith(SpringJUnit4ClassRunner.class) >>>>>> @ContextConfiguration(classes = CamelConfig.class) >>>>>> public class FooRouteTests extends CamelTestSupport { >>>>>> >>>>>> @Test >>>>>> public void testAdvised() throws Exception { >>>>>> context.getRouteDefinition("routeId").adviceWith(context, new >>>>>> RouteBuilder() { >>>>>> @Override >>>>>> public void configure() throws Exception { >>>>>> // intercept sending to mock:foo and do >>>>>> something else >>>>>> interceptSendToEndpoint("sql:*") >>>>>> .skipSendToOriginalEndpoint() >>>>>> .to("log:foo") >>>>>> .to("mock:advised"); >>>>>> } >>>>>> }); >>>>>> // we must manually start when we are done with all the advice >>>>>> with >>>>>> context.start(); >>>>>> >>>>>> template.sendBody("direct:foo", "bar"); >>>>>> >>>>>> getMockEndpoint("mock:advised").expectedMessageCount(1); >>>>>> assertMockEndpointsSatisfied(); >>>>>> } >>>>>> >>>>>> @Override >>>>>> public boolean isUseAdviceWith() { >>>>>> // tell we are using advice with, which allows us to advice >>>>>> the route >>>>>> // before Camel is being started, and thus can replace sql >>>>>> with something else. >>>>>> return true; >>>>>> } >>>>>> >>>>>> On Jun 11, 2014, at 12:16 PM, Claus Ibsen <[email protected]> wrote: >>>>>> >>>>>>> Hi >>>>>>> >>>>>>> Yeah if you have Camel in Action book, read chapter 6. >>>>>>> >>>>>>> And see bottom of this page >>>>>>> http://camel.apache.org/testing >>>>>>> >>>>>>> The advice builder is quite nifty and can "rework" the routes before >>>>>>> testing. >>>>>>> >>>>>>> >>>>>>> On Wed, Jun 11, 2014 at 8:10 PM, Matt Raible <[email protected]> >>>>>>> wrote: >>>>>>>> Hello, >>>>>>>> >>>>>>>> I have a route that looks as follows: >>>>>>>> >>>>>>>> from(uri) >>>>>>>> .to("log:input") >>>>>>>> >>>>>>>> .recipientList(simple("direct:${header.operationName}")); >>>>>>>> from("direct:lookup") >>>>>>>> .process(new Processor() { >>>>>>>> public void process(Exchange >>>>>>>> exchange) throws Exception { >>>>>>>> // grab parameters from >>>>>>>> request and set as headers for SQL statement >>>>>>>> } >>>>>>>> }) >>>>>>>> >>>>>>>> .recipientList(simple("sql:{{sql.lookup}}")).delimiter("false") >>>>>>>> .to("log:output") >>>>>>>> .process(new Processor() { >>>>>>>> public void process(Exchange >>>>>>>> exchange) throws Exception { >>>>>>>> List<HashMap> data = >>>>>>>> (ArrayList<HashMap>) exchange.getIn().getBody(); >>>>>>>> >>>>>>>> // convert data to response >>>>>>>> >>>>>>>> >>>>>>>> exchange.getOut().setBody(response); >>>>>>>> } >>>>>>>> }) >>>>>>>> >>>>>>>> Is it possible to unit test this route and mock the data returned from >>>>>>>> the "sql" call? It'd love to be able to verify headers after the first >>>>>>>> .process, mock the results from the SQL call and verify the results >>>>>>>> from the 2nd .process method. >>>>>>>> >>>>>>>> All of the routes I've developed with Camel so far make SQL calls, but >>>>>>>> I see SOAP calls in the future. I'll eventually need to mock SOAP >>>>>>>> calls as well. >>>>>>>> >>>>>>>> Thanks, >>>>>>>> >>>>>>>> Matt >>>>>>> >>>>>>> >>>>>>> >>>>>>> -- >>>>>>> Claus Ibsen >>>>>>> ----------------- >>>>>>> Red Hat, Inc. >>>>>>> Email: [email protected] >>>>>>> Twitter: davsclaus >>>>>>> Blog: http://davsclaus.com >>>>>>> Author of Camel in Action: http://www.manning.com/ibsen >>>>>>> hawtio: http://hawt.io/ >>>>>>> fabric8: http://fabric8.io/ >>>>>> >>>>> >>>> >>> >> > > > > -- > Claus Ibsen > ----------------- > Red Hat, Inc. > Email: [email protected] > Twitter: davsclaus > Blog: http://davsclaus.com > Author of Camel in Action: http://www.manning.com/ibsen > hawtio: http://hawt.io/ > fabric8: http://fabric8.io/
