grails集成测试中的模拟方法继续进行其他测试

时间:2016-01-08 16:20:05

标签: grails mocking integration-testing

我有一个集成测试,我有时想要模拟服务方法的返回。但是,我已经看到,一旦我模拟了该方法,调用它的后续测试也将使用模拟函数。

这是正常的吗?如果是这样,我怎样才能进行有时使用模拟函数的测试,有时候会使用真正的实现?

这是我的代码:

MyController {
    def someService

    def save(){
        ...
        def val = someService.methodToMock()//sometimes want to mock other times, not
        ...
    }
}

MyTest {

    def "test 1"(){
        ...
        //I want to mock here
        myController.someService.metaClass.methodToMock = { [] }
        ...
        myController.save()
    }

    def "test 2"(){
        ...
        //I don't want to mock here, however 
        // it is returning the mocked results
        myController.save()
    }
}

2 个答案:

答案 0 :(得分:5)

一般情况下,您不希望在集成或功能测试中更改与元类相关的任何内容,仅在单元测试中。预计您将在单元测试中执行此操作,并且在每次测试后或每个测试类运行后根据Grails的版本以及事物的方式自动支持还原原始元类配置。但在集成测试中并非如此。

您可以使用几种不同的方法。如果使用无类型依赖注入,例如def "test 1"() { ... def mockedService = [methodToMock: { args -> return ... }] myController.someService = mockedService ... myController.save() } ,然后你可以用你想要的任何东西覆盖真实的服务实例,只要它有你在测试方法中调用的方法,控制器就不会知道或者关心它不是真正的服务。

我喜欢在这种情况下使用闭包映射,因为Groovy将调用闭包,就像它是一个方法一样。所以对于测试1'你可以这样做:

someService.methodToMock()

这是有效的,因为您为每个测试获得了一个新的控制器实例,并且您只为该实例更改了服务,但实际服务根本没有受到影响。

您的控制器调用someService.get('methodToMock').call(),实际上是SomeService someService,但是地图访问和闭包调用语法可以利用Groovy的语法糖看起来像常规方法调用。

另一个选择是子服务服务并覆盖您想要的方法,并用它替换注入的实例。如果您键入依赖项注入(例如class TestSomeService extends SomeService { ... }),则必须使用此类似的功能。创建一个命名子类(def "test 1"() { ... def mockedService = new SomeService() { def methodToMock(args) { return ... } } myController.someService = mockedService ... myController.save() } )或创建一个匿名内部类:

var hello = new popup([{text:"Close"}], "Hello!", "<p>Hello World</p>");
hello.open();

答案 1 :(得分:1)

在一次测试中更改metaClass绝对会影响其他测试。你正在改变groovy系统,如果你是metaClassing,需要进行一些特殊的清理工作。最后我的方法我在metaClass中,我调用一个函数来撤销metaClass更改,传入metaClassed类的名称,并且实例metaClassed(如果有的话)。

def "some authenticated method test"() {
    given:
        def user = new UserDomain(blah blah blah)
        controller.metaClass.getAuthenticatedUser = { return user } 
    when:
        controller.authenticatedMethod() // which references the authenticated user
    then:
        // validate the results
    cleanup:
        revokeMetaClassChanges(theControllerClass, controller)
}

private def revokeMetaClassChanges(def type, def instance = null) {
    GroovySystem.metaClassRegistry.removeMetaClass(type)
    if (instance != null) {
        instance.metaClass = null
    }
}

或者,您可以在测试中模拟服务。类似于Burt提到的方法可能是:

def "some test"() {
    given:
        def mockSomeService = mockFor(SomeService)
        mockSomeService.demand.methodToMock(1) { def args ->
            return []
        }
        controller.someService = mockSomeService.createMock()
    when:
        controller.save()
    then:
        // implement your validations/assertions
}