Spock不使用Groovy MetaClass更改为服务测试

时间:2014-05-02 23:01:43

标签: grails groovy spock metaclass

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

重复尝试使用metaClass失败了:

  • String.metaClass.blarg生成错误No such property: blarg for class: java.lang.String
  • service.metaClass.getGithubUrlForPath修改服务实例不起作用
  • GithubService.metaClass.getGithubUrlForPath修改服务类不起作用
  • 尝试在测试方法设置中的metaClass上添加/修改方法,以及何时块都没有按预期工作

测试:

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

我已经测试了groovy shell中的String.metaClass.blarg示例(由grails启动),并且按预期进行了测试。

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

2 个答案:

答案 0 :(得分:3)

这就是如何编写测试以使它们通过:

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']
}

答案 1 :(得分:0)

为什么不使用Spock强大的Mocking能力?

查看http://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html#_creating_mock_objects

没有必要窥视元类本身,你可以创建一些存根对象,并且将调用所需的方法而不是原始方法。你也可以使用Groovy的MockFor和StubFor,它们可以更容易一些。

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

  1. 里面有一些复杂的逻辑,很容易让事情搞得一团糟。尝试在调试器下运行一些测试,你会看到它。
  2. Spock在引擎盖下使用了元类。它可以覆盖你自己的尝试。