Spock 不使用 Groovy MetaClass 对待测服务的更改
Posted
技术标签:
【中文标题】Spock 不使用 Groovy MetaClass 对待测服务的更改【英文标题】:Groovy MetaClass change to Service Under Test is not used by Spock 【发布时间】:2014-06-19 17:35:09 【问题描述】:在 Spock 单元测试中,我试图测试独立于 getGithubUrlForPath
的方法 findRepositoriesByUsername
的行为,它们都属于同一个服务。
多次尝试使用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(由 grails 启动)中测试了 String.metaClass.blarg
示例,结果符合预期。
我在这里有一个根本的误解吗?我究竟做错了什么?是否有更好的方法来处理所需的测试(替换被测服务上的方法)?
【问题讨论】:
【参考方案1】:这是编写测试以使其通过的方式:
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']
【讨论】:
谢谢!blarg
错误应该从错误中显而易见,但我完全错过了。 getGithubUrlForPath
问题似乎是闭包参数中缺少类型。现在这是有道理的!我想添加了一个新方法getGithubUrlForPath(Object pathParts)
,而不是替换现有的方法。【参考方案2】:
你为什么不使用 Spock 强大的 Mocking 能力呢?
看http://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html#_creating_mock_objects
无需窥视元类本身,您可以创建一些存根对象,然后调用所需的方法而不是原来的方法。你也可以使用 Groovy 的 MockFor 和 StubFor,它们会更容易一些。
您不能完全信任 spock 测试规范中的元类。
-
里面有一些复杂的逻辑,很容易把事情搞砸。尝试在调试器下运行一些测试,你会看到的。
Spock 在后台使用元类。它可以覆盖您自己的尝试。
【讨论】:
我想我仍然缺少一些东西......findRepositoriesByUsername
和 getGithubUrlForPath
都属于同一个服务(正在测试中),前者调用后者。当findRepositoriesByUsername
调用它时,我不明白如何模拟来自getGithubUrlForPath
的响应。以上是关于Spock 不使用 Groovy MetaClass 对待测服务的更改的主要内容,如果未能解决你的问题,请参考以下文章
Compile Groovy/Spock with GMavenPlus