0

I want to integration test a service method that calls a method that uses @Transactional(propagation = Propagation.REQUIRES_NEW). However, assertions based on the inner (new) transaction fail.

class MyService {

    @Transactional(propagation = Propagation.NESTED)
    def method1() {
        method2()
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    def method2() {
        // some code that will modify the DB
    }
} 

class MyServiceNonTransactionalIntegrationSpec extends IntegrationSpec {
    static transactional = false

    def myService = new MyService()

    setup() {
        // populate database here
    }

    cleanup() {
        // teardown database here after tests complete
    }

    def method1() {
        when: "we test the main method"
            myService.method1()

        then: "we should be able to see the result of method 1 and method 2"
           // BUT: (!!!)
           // assertions based on state of database fail, perhaps because new transaction
           // wrapped around method 2 has not yet committed
    }
}

How do I integration test method1()?

EDIT:

In an attempt to work around the proxy issue, I've tried the following, but still can't get it to work:

class MyService {

    def myService2

    @Transactional(propagation = Propagation.NESTED)
    def method1() {
        myService2.method2()
    }    
} 

class MyService2 {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    def method2() {
        // some code that will modify the DB
    }
} 

class MyServiceNonTransactionalIntegrationSpec extends IntegrationSpec {
    static transactional = false

    def myService = new MyService()
    def myService2 = new MyService2()

    setup() {
        myService.myService2 = myService2
        // populate database here
    }

    // rest as before
}
John
  • 10,837
  • 17
  • 78
  • 141

2 Answers2

1

It's only a self-invoking problem if you're using org.springframework.transaction.annotation.Transactional - you should be using @grails.transaction.Transactional instead which has the same configuration options but uses AST transforms instead of runtime proxies to avoid the issue of not calling the proxied method. When using the Grails annotation each method is rewritten to simulate creating a proxy for each one, wrapping the code in a transaction template that's configured based on the annotation settings for that method (either explicit on the method, or inferred from a class-scope annotation).

But your tests are broken because you're using new to create the services. Always use dependency injection in integration tests so you get the configured Spring bean, not just a new uninitialized POGO. To do this, add the same property syntax you would outside of your tests:

def myService
def myService2

and remove unnecessary wiring code (e.g. myService.myService2 = myService2) that Spring does for you.

Burt Beckwith
  • 75,342
  • 5
  • 143
  • 156
0

You've faced self-invoking. Look here: Spring - @Transactional - What happens in background? or Spring @Transactional Annotation : Self Invocation

Here is excerption from one of my classes:

@Service("authzService")
public class AuthorizationService implements IAuthorizationService {

    @Resource(name="authzService")
    private IAuthorizationService self;

I use this private member for self invoke methods with @Cached annotation.

Community
  • 1
  • 1
diafour
  • 51
  • 3
  • Thanks - I've amended the code to separate the methods into 2 classes and is still doesn't seem to work, see above. – John Apr 21 '15 at 12:46