5

What is the best practice to test a Grails Service which depends on another Service? The default mixin TestFor correctly inject the service under test, for eg:

@TestFor(TopService) 
class TopServiceTests {
    @Test
    void testMethod() {
        service.method()
    }
}

but if my instance of TopService (service) relies on another Service, like InnerService:

class TopService {
    def innerService
}

innerService will not be available, dependency injection doesn't seem to fill this variable. How should I proceed?

Wavyx
  • 1,715
  • 2
  • 14
  • 23

2 Answers2

8

Integration tests should not use the @TestFor annotation, they should extend GroovyTestCase. The test annotations are only for unit tests (and will have bad behavior when used in integration tests, especially the @Mock annotations). You're seeing one of those bad behaviors now.

If you extend GroovyTestCase you can then just have

def topService

At the top of your test and it'll get injected with all of it's dependencies injected.

For a unit test case, you'd just want to add new instances of associated services to your service in a setUp method. Just like:

@TestFor(TopService) 
class TopServiceTests {
    @Before public void setUp() {
        service.otherService = new OtherService()
    }
    ...
Ted Naleid
  • 26,511
  • 10
  • 70
  • 81
  • Thanks! I finally understood it with your explanation ;) I was a bit misleaded by the comments from http://stackoverflow.com/questions/2272677/dependency-injection-in-grails-integration-tests "Integration tests should not extend GrailsUnitTestCase". Not sure I fully get the differences between GroovyTestCase and GrailsUnitTestCase... – Wavyx Jun 14 '12 at 13:39
  • 1
    The biggest difference (that ends up biting people) is that the unit test case does a lot more in the way of mocking and messing with the metaClass on setUp and tearDown. Mocking is something that integration tests don't need as they've already got real things injected. It also means that when the integration tests finishes, it's bad for the unit test tearDown to be run as it can remove changes to the metaClass (like dynamic GORM methods) that are supposed to be there and that subsequent integration tests are expecting. – Ted Naleid Jun 15 '12 at 04:22
1

I have a CustomerRegistrationServiceTest and my CustomerRegistrationService depends on the PasswordService.

my CustomerRegistrationService just autowires it like normal:

class CustomerRegistrationService {
    def passwordService

In my CustomerRegistrationServiceTest I have:

@TestFor(CustomerRegistrationService)
@Mock(Customer)
class CustomerRegistrationServiceTests extends GrailsUnitTestMixin {

    void setUp() {
        mockService(PasswordService)
    }

So when I test the CustomerRegistrationService, it is able to access the PasswordService

klogd
  • 1,118
  • 11
  • 19
  • 1
    Thanks. Indeed, mocking seems to be the way to go, but more for a unit-test IMHO. For integration tests I'd rather test the full-fledged environment. – Wavyx Jun 14 '12 at 13:31