繁体   English   中英

Grails集成测试中的模拟方法会转移到其他测试中

[英]Mocking methods in grails integration test carries over to other tests

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

这正常吗? 如果是这样,我如何进行测试,有时使用模拟函数,有时使用实际实现?

这是我的代码:

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()
    }
}

通常,您不希望仅在单元测试中更改与集成或功能测试中的元类有关的任何内容。 预计您将在单元测试中进行此操作,并且自动支持在每次测试之后或在每个测试类运行之后还原原始元类,具体取决于Grails的版本和配置方式。 但这不是集成测试中的情况。

您可以使用几种不同的方法。 如果您使用无类型的依赖项注入,例如def someService ,那么您可以用任何您想要的东西覆盖真实的服务实例,只要它具有在测试方法期间要调用的方法,控制器就不会知道或关心这不是真正的服务。

在这种情况下,我喜欢使用闭包映射,因为Groovy会像调用方法一样调用闭包。 因此,对于“测试1”,您可以这样做:

def "test 1"() {
    ...
    def mockedService = [methodToMock: { args -> return ... }]
    myController.someService = mockedService
    ...
    myController.save()
}

之所以可行,是因为您为每个测试都获得了一个控制器的新实例,并且只为该实例更改了服务,但是真正的服务根本不受影响。

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

另一个选择是子类化服务并覆盖所需的方法,并用该方法替换注入的实例。 如果您键入依赖项注入(例如SomeService someService ),则可能需要此操作或类似操作。 创建一个命名子类( class TestSomeService extends SomeService { ... } )或创建一个匿名内部类:

def "test 1"() {
    ...
    def mockedService = new SomeService() {
        def methodToMock(args) {
            return ...
        }
    }
    myController.someService = mockedService
    ...
    myController.save()
}

在一个测试中更改metaClass绝对会影响其他测试。 您正在更改常规系统,并且如果要进行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
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM