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

多次尝试使用 metaClass 都失败了:

  • String.metaClass.blarg 产生错误 No such property: blarg for class: java.lang.String
  • service.metaClass.getGithubUrlForPath 修改服务实例不起作用
  • GithubService.metaClass.getGithubUrlForPath 修改服务类不起作用
  • 尝试在测试方法的设置和 when 块中的 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(由 grails 启动)中测试了 String.metaClass.blarg 示例,它按预期运行。

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

    最佳答案

    这是编写测试以使其通过的方式:

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

    关于grails - Spock 不使用 Groovy MetaClass 更改为 Service Under Test,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23438216/

    10-11 09:05