如何测试grails 3控制器与其他服务注入其中的服务?

时间:2016-03-04 17:33:18

标签: unit-testing grails grails-3.0.10

在控制器的单元测试用例中,我只能注入相应控制器中使用的服务。但是如果控制器中的一个服务注入另一个服务,那么我的测试用例就会失败,即无法调用null上的服务方法对象

@TestFor(MyController)
@Mock(MyService)
class MyControllerSpec extends Specification {
    void "test something"(){
        when:
            controller.method1();
        then:
            //something
    }

}

class MyController(){
    MyService myService

    void method1(){
        myService.serviceMethod()       
    }   
}

class MyService(){
    AnotherService anotherService
    void serviceMethod(){
        anotherService.anotherServiceMethod()
    }
}

class AnotherService(){
    void anotherServiceMethod(){
    \\something
    }
}

在这种情况下,我得到无法在null对象上调用“anotherServiceMethod”。 有没有办法测试这种类型的控制器?在其他服务中注入服务是一种好方法吗?

1 个答案:

答案 0 :(得分:5)

It is a good approach to inject serice into another service, nothing wrong with that.

To make this test working there are few approaches.

Recommended - unit test should test behaviour of only single class, if you need to test full functionality, integration/functional spec is better to do that. In such case, you execute methods of your controller, but all other classes which are called are mocks on which you predict what values are returned. Then you create separate tests for MyService and AnotherService. In such case, your test could look like:

@TestFor(MyController)
class MyControllerSpec extends Specification {
    void "test something"(){
        given:
            MyService myService = Mock()
            controller.myService = myService
        when:
            controller.method1();
        then:
            1 * myService.serviceMethod() >> someResult
            //something
    }
}

this test ensures, that serviceMethod() is called and you force it to return something what you expect for this spec. If in other case (exception thrown, if/else you want to be sure that serviceMethod() is not called, you can use 0 * myService.serviceMethod()

Not recommended: If you insist that service method should be called in this spec, you can create mock of AnotherService and set it on the service which is available in controller. Something like:

AnotherService anotherService = Mock()
controller.myService.anotherService = anotherService
...
then:
1 * anotherService.callAnotherMethod()

Maybe also using @Mock([MyService, AnotherService]) will work, but I didn't test it. We test integrations with integration tests - there everything is injected for you and you work on normal classes.