Search code examples
unit-testinggrailsservicemockingspock

Grails Spock unit test requires to mock transaction manager


In Grails 3.1.12, I want to unit test a service:

@Transactional
class PlanService {
    List<Plan> getPlans(Map params) {
        def currentUser = (User)springSecurityService.getCurrentUser()
        return Plan.findAllByCompany(currentUser.employer, params)
    }
}

Like this:

@TestFor(PlanService)
@Mock([Plan, User, Company])
class PlanServiceSpec extends Specification {
    void "Retrieve plan from the current user"() {
        setup:
        // create and save entities here

        when: "the plans are retrieved"
        def params = null
        def plans = service.getPlans(params)

        then: "the result should only include plans associated to the current user's company"
        plans.size() == 2
}

Running the test from the console:

grails> test-app my.PlanServiceSpec -unit

Fails with:

my.FundingPlanServiceSpec > Retrieve plan from the current user FAILED
java.lang.IllegalStateException at PlanServiceSpec.groovy:48

and in the test report (HTML):

java.lang.IllegalStateException: No transactionManager was specified.
Using @Transactional or @Rollback requires a valid configured transaction manager.
If you are running in a unit test ensure the test has been properly configured
and that you run the test suite not an individual test method.

Now if I comment out the @Transactional annotation in the service, the test passes, but that's not the intended implementation. I am able to work around the problem by mocking the transaction manager:

service.transactionManager = Mock(PlatformTransactionManager) {
    getTransaction(_) >> Mock(TransactionStatus)
}

But this seems very awkward, if not wrong.

Is there some incantation I forgot to invoke?

EDIT: looks similar to an old bug, but it's been closed more than a year.


Solution

  • Have you tried what a comments says that fixes the problem? If not, try to annotate the test class with:

    @TestMixin(DomainClassUnitTestMixin)
    

    and then:

    service.transactionManager = getTransactionManager()