You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@camel.apache.org by Stephan Burkard <sb...@gmail.com> on 2013/12/30 09:36:16 UTC

Question about using message headers/Exchange properties and route testing

Hi Camel users

To process arbitrary logic in my Camel routes, I use simple bean calls.
That is fine because I can test all these pieces of logic with standard
unit tests.

However, often the bean calls produce results that are saved in the message
header (if I need to save it for later) or Exchange properties (if it is
only needed in the current route).

So my bean methods often look like this:
public void setDocumentGuid(@Headers Map<String, Object> headers);

The header-map is passed to add new headers, the method is simply called
with "bean(bean, method)" in the route.

My problem arised in the route tests where I mock away the whole bean. I
can mock the method call, but I cannot add entries to the header- or
property-maps. So if the header or property entry is expected later in the
route, it fails.

Therefore I moved to bean methods like this:
public String setDocumentGuid();

The header-map is no more needed, since the header value is returned and
the method is called with "setHeader(headerKey, bean(bean, method)" in the
route. Now I can return whatever I like from the mock.

But the problem returns if

#1: I have multiple values or objects I would like to save. I cannot return
them in the method (without creating a "transport object" or returning a
map), so I need to add them to the headers or properties in the method, but
then they are not existing when I mock the method call.

#2: I would like to test a subroutes (direct:) that is expecting Exchange
properties from the parent route. I cannot easily set these properties for
the tests of the subroute (I probably could by injecting a processor for
the test that sets them, but the most simple test would become quite
complex).

I would like to only test the correct message flow with route tests and
therefore mock away all beans (that are already tested with unit-tests),
but since my beans set message headers or properties they are like a
route-extension and needed to execute the route correctly.

Am I missing a basic Camel concept? How do you draw the line between route
with headers/properties and "external" components like beans?

Thanks for any help
Stephan

Re: Question about using message headers/Exchange properties and route testing

Posted by Stephan Burkard <sb...@gmail.com>.
Thank you very much Claus, great hints!

I found the ExchangeBuilder in Camel 2.11 and later, so it is not available
in my version, but I have to keep that in mind.

However, I found a CamelTestSupport.createExchangeWithBody() method that
seems to create an Exchange from my body object. Then I could add my
headers and properties and use the send-method that takes an Exchange. Or I
use the send-method that takes a processor as you also suggested.

Regards
Stephan


On Fri, Jan 3, 2014 at 1:40 PM, Henryk Konsek <he...@gmail.com> wrote:

> > There is also an ExchangeBuilder AFAIR that you may use as well. Then
> > you can use that to create an exchange, to send with the produce
> > template.
>
> ExchangeBuilder is cool indeed, I forgot about it. :)
>
> BTW Probably having bean mocking API similar to the one presented
> below would be a nice addition to our testing API. However creating
> such API is not as trivial as it might look like, because of the rich
> bean binding capabilities that it should support.
>
> @Bean // Spring Java config Example
> MyBean myBean() {
>   return MockBean.createMockBean(MyBean.class).
>     removeHeader("foo"). // this mock will remove given header from
> exchange
>     addProperty("prop"). // add property that couldn't be added before
>     assertHeader("bar").isEqualTo("baz"); // and make some assertions
> }
>
> from(...).bean("myBean", "myMethod").to(...);
>
> --
> Henryk Konsek
> http://henryk-konsek.blogspot.com
>

Re: Question about using message headers/Exchange properties and route testing

Posted by Henryk Konsek <he...@gmail.com>.
> There is also an ExchangeBuilder AFAIR that you may use as well. Then
> you can use that to create an exchange, to send with the produce
> template.

ExchangeBuilder is cool indeed, I forgot about it. :)

BTW Probably having bean mocking API similar to the one presented
below would be a nice addition to our testing API. However creating
such API is not as trivial as it might look like, because of the rich
bean binding capabilities that it should support.

@Bean // Spring Java config Example
MyBean myBean() {
  return MockBean.createMockBean(MyBean.class).
    removeHeader("foo"). // this mock will remove given header from exchange
    addProperty("prop"). // add property that couldn't be added before
    assertHeader("bar").isEqualTo("baz"); // and make some assertions
}

from(...).bean("myBean", "myMethod").to(...);

-- 
Henryk Konsek
http://henryk-konsek.blogspot.com

Re: Question about using message headers/Exchange properties and route testing

Posted by Claus Ibsen <cl...@gmail.com>.
Just use the send/request method that takes a processor, and then use
an inlined processor to set the headers/properties/body on the
exchange in message.

There is also an ExchangeBuilder AFAIR that you may use as well. Then
you can use that to create an exchange, to send with the produce
template.

On Fri, Jan 3, 2014 at 12:41 PM, Stephan Burkard <sb...@gmail.com> wrote:
> Hi Henryk
>
>> Are you sure? :) Exchange properties are propagated as well as
>> headers. Can you show me an example, where the properties are not
>> propagated down the route?
>
> Wow, I just realized that there are producer methods to send a body and a
> property. I never noticed them before because I always used the ones for
> body and header(s).
>
> But however, I need to send expected headers into most of my tests, so I
> would need to have a method that takes body, header-map and a property or
> even a property-map. At least in my current Camel version (2.9.x) I didn't
> found a way to do this. Am I wrong? Have you another pleasant surprise for
> me? :)
>
> Thanks a lot!
> Stephan
>
>
>
> On Tue, Dec 31, 2013 at 11:06 AM, Henryk Konsek <he...@gmail.com> wrote:
>
>> > @Henryk: Yes, that works fine for message headers, I use it a lot to pass
>> > expected message headers. But it is not possible for Exchange properties.
>>
>> Are you sure? :) Exchange properties are propagated as well as
>> headers. Can you show me an example, where the properties are not
>> propagated down the route?
>>
>> > @Willem: Yes, that's true. It feels a bit "cumbersome", but it works.
>>
>> Yeah, the whole point of using mocking library is to reduce the
>> boilerplate, so I won't implement interfaces as well. Also not always
>> you would like to introduce interface for the bean and tests design
>> shouldn't force you to do so. There is also a chance that you might
>> work with 3rd party beans (developed by the other team for example)
>> which you can't refactor. You definitely need a way to live with the
>> mocks in your routes :) .
>>
>> > Based on your answers, I guess there is no fundamental Camel concept I am
>> > missing, but simply the Camel best practices that need to mature in my
>> > Camel routes and tests :-)
>>
>> Yes. There is no hidden Camel feature that could help you here. :)
>>
>> --
>> Henryk Konsek
>> http://henryk-konsek.blogspot.com
>>



-- 
Claus Ibsen
-----------------
Red Hat, Inc.
Email: cibsen@redhat.com
Twitter: davsclaus
Blog: http://davsclaus.com
Author of Camel in Action: http://www.manning.com/ibsen
Make your Camel applications look hawt, try: http://hawt.io

Re: Question about using message headers/Exchange properties and route testing

Posted by Stephan Burkard <sb...@gmail.com>.
Hi Henryk

> Are you sure? :) Exchange properties are propagated as well as
> headers. Can you show me an example, where the properties are not
> propagated down the route?

Wow, I just realized that there are producer methods to send a body and a
property. I never noticed them before because I always used the ones for
body and header(s).

But however, I need to send expected headers into most of my tests, so I
would need to have a method that takes body, header-map and a property or
even a property-map. At least in my current Camel version (2.9.x) I didn't
found a way to do this. Am I wrong? Have you another pleasant surprise for
me? :)

Thanks a lot!
Stephan



On Tue, Dec 31, 2013 at 11:06 AM, Henryk Konsek <he...@gmail.com> wrote:

> > @Henryk: Yes, that works fine for message headers, I use it a lot to pass
> > expected message headers. But it is not possible for Exchange properties.
>
> Are you sure? :) Exchange properties are propagated as well as
> headers. Can you show me an example, where the properties are not
> propagated down the route?
>
> > @Willem: Yes, that's true. It feels a bit "cumbersome", but it works.
>
> Yeah, the whole point of using mocking library is to reduce the
> boilerplate, so I won't implement interfaces as well. Also not always
> you would like to introduce interface for the bean and tests design
> shouldn't force you to do so. There is also a chance that you might
> work with 3rd party beans (developed by the other team for example)
> which you can't refactor. You definitely need a way to live with the
> mocks in your routes :) .
>
> > Based on your answers, I guess there is no fundamental Camel concept I am
> > missing, but simply the Camel best practices that need to mature in my
> > Camel routes and tests :-)
>
> Yes. There is no hidden Camel feature that could help you here. :)
>
> --
> Henryk Konsek
> http://henryk-konsek.blogspot.com
>

Re: Question about using message headers/Exchange properties and route testing

Posted by Henryk Konsek <he...@gmail.com>.
> @Henryk: Yes, that works fine for message headers, I use it a lot to pass
> expected message headers. But it is not possible for Exchange properties.

Are you sure? :) Exchange properties are propagated as well as
headers. Can you show me an example, where the properties are not
propagated down the route?

> @Willem: Yes, that's true. It feels a bit "cumbersome", but it works.

Yeah, the whole point of using mocking library is to reduce the
boilerplate, so I won't implement interfaces as well. Also not always
you would like to introduce interface for the bean and tests design
shouldn't force you to do so. There is also a chance that you might
work with 3rd party beans (developed by the other team for example)
which you can't refactor. You definitely need a way to live with the
mocks in your routes :) .

> Based on your answers, I guess there is no fundamental Camel concept I am
> missing, but simply the Camel best practices that need to mature in my
> Camel routes and tests :-)

Yes. There is no hidden Camel feature that could help you here. :)

-- 
Henryk Konsek
http://henryk-konsek.blogspot.com

Re: Question about using message headers/Exchange properties and route testing

Posted by Stephan Burkard <sb...@gmail.com>.
@Henryk: Yes, that works fine for message headers, I use it a lot to pass
expected message headers. But it is not possible for Exchange properties.

@Willem: Yes, that's true. It feels a bit "cumbersome", but it works.

Based on your answers, I guess there is no fundamental Camel concept I am
missing, but simply the Camel best practices that need to mature in my
Camel routes and tests :-)

Thanks a lot
Stephan


On Mon, Dec 30, 2013 at 10:22 AM, Willem Jiang <wi...@gmail.com>wrote:

> Hi,
>
> I don’t think you need to use mock framework to mock the bean.
> You can just write a simple bean which implement the bean’s interface to
> setup the message headers and exchange properties for you camel route.
>
> --
> Willem Jiang
>
> Red Hat, Inc.
> Web: http://www.redhat.com
> Blog: http://willemjiang.blogspot.com(http://willemjiang.blogspot.com/)
> (English)
> http://jnn.iteye.com(http://jnn.javaeye.com/) (Chinese)
> Twitter: willemjiang
> Weibo: 姜宁willem
>
>
>
> On December 30, 2013 at 4:36:53 PM, Stephan Burkard (sburkard@gmail.com)
> wrote:
> >
> > Hi Camel users
> >
> > To process arbitrary logic in my Camel routes, I use simple bean
> > calls.
> > That is fine because I can test all these pieces of logic with standard
> > unit tests.
> >
> > However, often the bean calls produce results that are saved
> > in the message
> > header (if I need to save it for later) or Exchange properties
> > (if it is
> > only needed in the current route).
> >
> > So my bean methods often look like this:
> > public void setDocumentGuid(@Headers Map
> > headers);
> >
> > The header-map is passed to add new headers, the method is simply
> > called
> > with "bean(bean, method)" in the route.
> >
> > My problem arised in the route tests where I mock away the whole
> > bean. I
> > can mock the method call, but I cannot add entries to the header-
> > or
> > property-maps. So if the header or property entry is expected
> > later in the
> > route, it fails.
> >
> > Therefore I moved to bean methods like this:
> > public String setDocumentGuid();
> >
> > The header-map is no more needed, since the header value is returned
> > and
> > the method is called with "setHeader(headerKey, bean(bean,
> > method)" in the
> > route. Now I can return whatever I like from the mock.
> >
> > But the problem returns if
> >
> > #1: I have multiple values or objects I would like to save. I cannot
> > return
> > them in the method (without creating a "transport object" or
> > returning a
> > map), so I need to add them to the headers or properties in the method,
> > but
> > then they are not existing when I mock the method call.
> >
> > #2: I would like to test a subroutes (direct:) that is expecting
> > Exchange
> > properties from the parent route. I cannot easily set these properties
> > for
> > the tests of the subroute (I probably could by injecting a processor
> > for
> > the test that sets them, but the most simple test would become
> > quite
> > complex).
> >
> > I would like to only test the correct message flow with route tests
> > and
> > therefore mock away all beans (that are already tested with unit-tests),
> > but since my beans set message headers or properties they are
> > like a
> > route-extension and needed to execute the route correctly.
> >
> > Am I missing a basic Camel concept? How do you draw the line between
> > route
> > with headers/properties and "external" components like beans?
> >
> > Thanks for any help
> > Stephan
> >
>
>

Re: Question about using message headers/Exchange properties and route testing

Posted by Willem Jiang <wi...@gmail.com>.
Hi,

I don’t think you need to use mock framework to mock the bean.
You can just write a simple bean which implement the bean’s interface to setup the message headers and exchange properties for you camel route.

--  
Willem Jiang

Red Hat, Inc.
Web: http://www.redhat.com
Blog: http://willemjiang.blogspot.com(http://willemjiang.blogspot.com/) (English)
http://jnn.iteye.com(http://jnn.javaeye.com/) (Chinese)
Twitter: willemjiang  
Weibo: 姜宁willem



On December 30, 2013 at 4:36:53 PM, Stephan Burkard (sburkard@gmail.com) wrote:
>  
> Hi Camel users
>  
> To process arbitrary logic in my Camel routes, I use simple bean  
> calls.
> That is fine because I can test all these pieces of logic with standard  
> unit tests.
>  
> However, often the bean calls produce results that are saved  
> in the message
> header (if I need to save it for later) or Exchange properties  
> (if it is
> only needed in the current route).
>  
> So my bean methods often look like this:
> public void setDocumentGuid(@Headers Map  
> headers);
>  
> The header-map is passed to add new headers, the method is simply  
> called
> with "bean(bean, method)" in the route.
>  
> My problem arised in the route tests where I mock away the whole  
> bean. I
> can mock the method call, but I cannot add entries to the header-  
> or
> property-maps. So if the header or property entry is expected  
> later in the
> route, it fails.
>  
> Therefore I moved to bean methods like this:
> public String setDocumentGuid();
>  
> The header-map is no more needed, since the header value is returned  
> and
> the method is called with "setHeader(headerKey, bean(bean,  
> method)" in the
> route. Now I can return whatever I like from the mock.
>  
> But the problem returns if
>  
> #1: I have multiple values or objects I would like to save. I cannot  
> return
> them in the method (without creating a "transport object" or  
> returning a
> map), so I need to add them to the headers or properties in the method,  
> but
> then they are not existing when I mock the method call.
>  
> #2: I would like to test a subroutes (direct:) that is expecting  
> Exchange
> properties from the parent route. I cannot easily set these properties  
> for
> the tests of the subroute (I probably could by injecting a processor  
> for
> the test that sets them, but the most simple test would become  
> quite
> complex).
>  
> I would like to only test the correct message flow with route tests  
> and
> therefore mock away all beans (that are already tested with unit-tests),  
> but since my beans set message headers or properties they are  
> like a
> route-extension and needed to execute the route correctly.  
>  
> Am I missing a basic Camel concept? How do you draw the line between  
> route
> with headers/properties and "external" components like beans?  
>  
> Thanks for any help
> Stephan
>  


Re: Question about using message headers/Exchange properties and route testing

Posted by Henryk Konsek <he...@gmail.com>.
Hi Stephan,

> My problem arised in the route tests where I mock away the whole bean. I
> can mock the method call, but I cannot add entries to the header- or
> property-maps. So if the header or property entry is expected later in the
> route, it fails.

Keep in mind that in tests you can send messages via ProducerTemplate
with some arbitatry headers/properties. Since headers and properties
are propagated with the messages down the route, you can set test
fixtures this way.

producerTemplate.sendBodyAndHeader("direct:test", "message",
"expected_header_key", "expected_header_value");

In production environment "expected_header_key" could be set by your
bean, but for unit tests you would pass it explicitly as a fixture to
the producer template initiating the test.

This approach usually works for me. For more complicated cases (for
example when you need to alter the header within the route), you can
use AdviceWith [1] or interceptors [2].

Cheers.

[1] http://camel.apache.org/advicewith.html
[2] http://camel.apache.org/intercept.html

-- 
Henryk Konsek
http://henryk-konsek.blogspot.com