我有一个groovy脚本,使用readline方法通过java.io.console对象向用户提出一些问题。另外,我用它来询问密码(用于设置HTTPS)。我如何使用Spock对这段代码进行单元测试?目前它抱怨该对象是Java最终对象,无法进行测试。显然,我不是第一个尝试这个的人,所以我想我会问。
代码草图如下所示:
class MyClass {
def cons
MyClass() {
cons = System.console()
}
def getInput = { prompt, defValue ->
def input = (cons.readLine(prompt).trim()?:defValue)?.toString()
def inputTest = input?.toLowerCase()
input
}
}
我希望单元测试能够测试是否可以返回一些模拟响应,并且可以返回默认值。注意:这是简化的,所以我可以弄清楚如何进行单元测试,getInput方法中还有更多的代码需要进行测试,但是一旦我清除了这个障碍应该没问题。
akhikhl编辑的每回答 根据这个建议,我做了一个简单的界面:
interface TestConsole {
String readLine(String fmt, Object ... args)
String readLine()
char[] readPassword(String fmt, Object ... args)
char[] readPassword()
}
然后我尝试了这样的测试:
def "Verify get input method mocking works"() {
def consoleMock = GroovyMock(TestConsole)
1 * consoleMock.readLine(_) >> 'validResponse'
inputMethods = new MyClass()
inputMethods.cons = consoleMock
when:
def testResult = inputMethods.getInput('testPrompt', 'testDefaultValue')
then:
testResult == 'validResponse'
}
我选择不改变构造函数,因为我不想改变我的实际代码只是为了测试它。幸运的是,Groovy让我用一个'def'定义控制台,所以我做的工作正常。
问题是以上不起作用!!!我无法抗拒 - 这不是逻辑! Spock在某个地方的GroovyMockMetaClass中获得了“迷失”。如果我在代码中更改了一行,而在测试中更改了一行就可以了。
代码更改:
From:
def input = (cons.readLine(prompt).trim()?:defValue)?.toString()
To: (add the null param)
def input = (cons.readLine(prompt, null).trim()?:defValue)?.toString()
测试更改:
From:
1 * consoleMock.readLine(_) >> 'validResponse'
To: (again, add a null param)
1 * consoleMock.readLine(_, null) >> 'validResponse'
然后测试终于起作用了。这是Spock中的一个错误,还是我只是在左外野?我不介意需要做任何可能需要的测试工具,但必须修改代码才能使这项工作真的非常糟糕。
答案 0 :(得分:3)
你是对的:因为Console类是final,所以无法扩展。因此,解决方案应该朝另一个方向发展:
创建新类MockConsole,不是从Console继承而是使用相同的方法。
以这种方式更改MyClass的构造函数:
MyClass(cons = null){ this.cons = cons?:System.console() }
在spock测试中实例化MockConsole并将其传递给MyClass构造函数。
<强>更新-201312272156 强>
我玩了一点spock。模拟“readLine(String fmt,Object ... args)”的问题似乎特定于varargs(或者最后arg是一个列表,这与groovy相同)。我设法将问题减少到以下情况:
定义界面:
interface IConsole {
String readLine(String fmt, Object ... args)
}
定义测试:
class TestInputMethods extends Specification {
def 'test console input'() {
setup:
def consoleMock = GroovyMock(IConsole)
1 * consoleMock.readLine(_) >> 'validResponse'
when:
// here we get exception "wrong number of arguments":
def testResult = consoleMock.readLine('testPrompt')
then:
testResult == 'validResponse'
}
}
此测试变体失败,异常“参数数量错误”。特别是,spock认为readLine接受2个参数而忽略了第二个参数是vararg的事实。证明:如果我们从IConsole.readLine中删除“Object ... args”,则测试成功完成。
这是解决方法(希望是暂时的)问题:将readLine的调用更改为:
def testResult = consoleMock.readLine('testPrompt', [] as Object[])
然后测试成功完成。
我也尝试过针对spock 1.0-groovy-2.0-SNAPSHOT的相同代码 - 问题是一样的。
<强>更新-201312280030 强>
varargs的问题解决了!非常感谢@charlesg,他在Spock: mock a method with varargs
回答了我的相关问题解决方案如下:用Mock替换GroovyMock,然后正确解释varargs。