Grails Spock未使用对测试中服务的Groovy元类更改

Grails Spock未使用对测试中服务的Groovy元类更改,grails,groovy,spock,metaclass,Grails,Groovy,Spock,Metaclass,在Spock单元测试中,我试图测试方法findRepositoriesBySerName的行为,该方法独立于getGithubUrlForPath,两者都属于相同的服务 重复尝试使用元类失败: String.metaClass.blarg生成一个错误没有这样的属性:blarg for class:java.lang.String 修改服务实例的service.metaClass.getGithBurlForPath无效 修改服务类的GithubService.metaClass.getGith

在Spock单元测试中,我试图测试方法
findRepositoriesBySerName
的行为,该方法独立于
getGithubUrlForPath
,两者都属于相同的服务

重复尝试使用
元类
失败:

  • String.metaClass.blarg
    生成一个错误
    没有这样的属性:blarg for class:java.lang.String
  • 修改服务实例的
    service.metaClass.getGithBurlForPath
    无效
  • 修改服务类的
    GithubService.metaClass.getGithubUrlForPath
    无效
  • 尝试在测试方法设置中的
    元类上添加/修改方法,当出现块时,这两种方法均未按预期工作
测试:

package grails.woot

import grails.test.mixin.TestFor

@TestFor(GithubService)
class GithubServiceSpec extends spock.lang.Specification {

    def 'metaClass test'() {
        when:
        String.metaClass.blarg = { -> 
            'brainf***'
        }

        then:
        'some string'.blarg == 'brainf***'
    }

    def 'can find repositories for the given username'() {
        given:
        def username = 'username'
        def requestPathParts

        when: 'the service is called to retrieve JSON'
        service.metaClass.getGithubUrlForPath = { pathParts ->
            requestPathParts = pathParts
        }
        service.findRepositoriesByUsername(username)

        then: 'the correct path parts are used'
        requestPathParts == ['users', username, 'repos']
    }

}
服务:

package grails.woot

import grails.converters.JSON

class GithubService {

    def apiHost = 'https://api.github.com/'

    def findRepositoriesByUsername(username) {
        try{
            JSON.parse(getGithubUrlForPath('users', username, 'repos').text)
        } catch (FileNotFoundException ex) {
            // user not found
        }
    }

    def getGithubUrlForPath(String ... pathParts) {
        "${apiHost}${pathParts.join('/')}".toURL()
    }
}
我已经在GroovyShell(由grails启动)中测试了
String.metaClass.blarg
示例,结果如预期所示


我在这里有一个基本的误解吗?我做错了什么?有没有更好的方法来处理所需的测试(替换被测服务上的方法)?

为什么不使用Spock强大的模拟功能

不需要窥视元类本身,您可以创建一些存根对象,并且将调用所需的方法而不是原始方法。您还可以使用Groovy的MockFor和StubFor,它们会更简单一些

您不能完全信任spock测试规范中的元类

  • 里面有一些复杂的逻辑,很容易把事情搞砸。尝试在调试器下运行一些测试,您将看到它
  • 斯波克在引擎盖下使用元类。它可以覆盖您自己的尝试

  • 以下是编写测试以使其通过的方法:

    def 'metaClass test'() {
        given:
            String.metaClass.blarg = { -> 'brainf***' }
    
        expect:
            // note blarg is a method on String metaClass 
            // not a field, invoke the method
            'some string'.blarg() == 'brainf***'
    }
    
    def 'can find repositories for the given username'() {
        given:
            def username = 'username'
            def requestPathParts
    
        when: 'the service is called to retrieve JSON'
            service.metaClass.getGithubUrlForPath = { String... pathParts ->
                requestPathParts = pathParts
                [text: 'blah'] // mimicing URL class
            }
            service.findRepositoriesByUsername(username)
    
        then: 'the correct path parts are used'
            requestPathParts == ['users', username, 'repos']
    }
    

    我想我还是错过了一些东西。。。
    findRepositoriesByUsername
    getGithubUrlForPath
    都属于同一个服务(测试中),前者调用后者。当
    findRepositoriesByUsername
    调用来自
    getGithubUrlForPath
    的响应时,我不明白如何模拟它。谢谢!从错误中可以明显看出,
    blarg
    错误,但我完全忽略了这一点。
    getGithubUrlForPath
    问题似乎是闭包参数中缺少类型。这是有道理的!我假设添加了一个新方法
    getGithubUrlForPath(objectpathparts)
    ,而不是替换现有的方法。