camel-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Minh Tran <darth.minhs...@gmail.com>
Subject Re: Can unit tests be @Transactional?
Date Mon, 22 Sep 2014 23:23:19 GMT
I think it’s important to understand the reason why your test fails, it’s not really camel’s
fault. It's because your camel route executes your dao method on a separate thread. If you’re
using a JMS consumer then this will definitely run under a separate thread.

This is a spring transaction “feature”, transactions are bound to the current thread and
if you have multiple threads then it will create separate transactions. Lets say you had a
spring unit test purely testing your dao method(minus all the camel stuff) and you put @Transactional
on the unit test. Then you did the same thing and seed the data, your test would run just
fine because your dao would participate in the same transaction as your unit test. But if
you change your dao to thread off and then execute a transaction then you’d have the same
blocking issue. So the problem isn’t explicitly camel, it’s spring’s rule of one transaction
per thread.

Alternatively think about using a mocked dao instead and not worry about these transactional
issues. Either by using @MockEndpointsAndSkip or inject via a third party mocking library
like mockito. The real dao method can be unit tested separately.

Or in your unit test, you can remove the jms component out using @UseAdviceWith and replace
with a direct component. This will ensure no threading will occur due to the jms consumer
when you execute the route in your unit test.

On 23 Sep 2014, at 1:41 am, James Green <james.mk.green@gmail.com> wrote:

> OK - we have a working solution. Someone should document this at
> http://camel.apache.org/transactional-client.html and
> http://camel.apache.org/spring-testing.html since we have had many days of
> trouble.
> 
> The crux is the source here:
> https://github.com/rajivj2/example2/blob/master/src/test/java/com/example/NotificationRouterIT.java
> 
> Essentially in our case the test runner is a Spring integration test that
> seeds data then tests a route that consumes a JMS message and processes it
> in a database.
> 
> First problem is that database access requires a transaction. We annotate
> our Processor classes @Transactional (remembering not to do so within the
> DAO, a common error) and crucially avoid annotating our test method
> @Transactional. Instead, the test method creates a TransactionTemplate from
> Spring, handing it the injected TransactionManager and execute the data
> seeding operation. Finally, send a test message and assert that the sun
> still shines.
> 
> If the test method itself is annotated @Transactional, as Minh infers, the
> seed data is held in memory until the test method completes. We were
> expecting Camel, using the shared TransactionManager, to see this data but
> instead is either hangs because of a separate transaction context, or just
> doesn't see anything if no transaction is used. Programming the transaction
> using the TransactionTemplate lets us commit the seed data before the end
> of the test, allowing Camel's transaction context to see database data
> written already.
> 
> Improvements and corrections welcome.
> 
> 
> On 22 September 2014 15:17, Minh Tran <darth.minhster@gmail.com> wrote:
> 
>> The reason for the hang is because Spring unit test transaction is
>> separate to the camel transaction. The camel transaction is blocked waiting
>> because the spring has uncommitted changes to the same table it is trying
>> to read from.
>> 
>> Either
>> 1. commit the spring transaction first, drop the @Transactional and use a
>> TransactionTemplate to do this OR
>> 2. Somehow get the spring and camel to participate in the same transaction
>> during the unit test. If they share the same transaction manager and camel
>> transaction is set to PROPAGATION_REQUIRED which is the default, this
>> should just work. This assumes your unit test and camel execution is
>> running under the same thread though. If not then stick to the first method.
>> 
>> On 22 Sep 2014, at 11:51 pm, James Green <james.mk.green@gmail.com> wrote:
>> 
>>> We have a Spring project that has a unit test annotated @Transactional.
>>> This uses a DAO to save a sample Entity before invoking a Camel route
>> that
>>> accepts a JMS message and sends it to some Processors.
>>> 
>>> The problem we have is when the test itself is annotated @Transactional.
>>> The route stops having received the message and begin performing a
>> database
>>> query - it literally hangs performing the SELECT. Naturally after 20s the
>>> time-out is hit.
>>> 
>>> If we remove @Transactional from the test the route continues but finds
>>> nothing in the database as the initial Entity save had no effect (it
>>> appears).
>>> 
>>> So we're clearly missing something here - how should we seed test data if
>>> @Transactional hangs and without it the data is not committed?
>>> 
>>> Thanks,
>>> 
>>> James
>> 
>> 


Mime
View raw message