6

I have a very simple setup - a controller that invokes a method on a service. In this service, I save an object to the DB and also send a JMS message to a queue - once it is saved - using an injected JMSTemplate. The service has transactions enabled by default.

When testing this manually - with the ActiveMQ server down - an exception is thrown and the transaction is rolled back - with the net effect that the object is also not saved to the DB. All good.

However, when I run this through an integration test (with ActiveMQ still down), the assert, that I have in place to check that the object did not get saved in the DB using a count query after I invoke the controller, fails saying that the count is 1. I have confirmed that the DB does not have any of these objects when the test starts by adding another assert at the beginning of the test to ensure that the count is 0.

Is this expected behaviour (maybe due to the nature of transactions in an integration test environment) or am I potentially doing something wrong? The exception is still being thrown because the JMS server is down - and it is a RuntimeException.

Grails integration tests and transactions gives an impression that this is to be expected - in which case, are there any suggestions around best practices for how to test transactional behaviour in integration tests?

Community
  • 1
  • 1
Rama
  • 93
  • 1
  • 7
  • If I remember correctly, the transaction isn't rolled back until after the test completes. So, when you are doing the assert, it looks like the data got saved to the db, because the transaction didn't get rolled back yet. – GreyBeardedGeek Mar 26 '12 at 03:50
  • Yes, that does seem the case since the same transaction seems to be used across - both the test and controller. Was wondering if there was a standard paradigm to use in cases like these. I don't want to change the controller code just for testing to make it require a new transaction. – Rama Mar 26 '12 at 04:18
  • 1
    This blog post - http://www.fepede.net/blog/?p=27 shows how to make the test non-transactional, which will cause the controller and service to have their own transaction (not use the test's transaction). The downside is that if an exception is not thrown, the transaction will commit, and you'll have to clean up yourself. – GreyBeardedGeek Mar 27 '12 at 04:53

1 Answers1

8

Not sure why everyone commented instead of answering as the comments do expose the answer. I'll jump in and try to snag the answer points!

Yes, in a Spock test the entire test, including whens, thens, givens, etc. are all run out of the same transaction. As such you will not see the rollback you are hoping for until after the completion of the test.

You can set your test to non-transactional by adding "static transactional = false" at the top of the test class. The fallout of course is that you will then need to clean up the database after your tests are run (if database sanitation is important to you). One big problem here is that the Grails IntegrationSpec class has a bug in it that will blow up whenever you try setting transactional to false. This SO has a solution to that problem:

Grails 2.3 IntegrationSpec cannot be transactional false

Basically, copy all of the code for grails.test.spock.IntegrationSpec into your own class and replace a few methods with versions that respect the transactional nature of the test.

Community
  • 1
  • 1
Glenn Filson
  • 384
  • 3
  • 10