【发布时间】:2013-12-26 18:50:07
【问题描述】:
我有一个 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 中的一个错误,还是我只是在左场?我不介意在测试工具中需要做任何可能需要做的事情,但是必须修改代码才能完成这项工作真的非常糟糕。
【问题讨论】:
-
仅供参考:忘了说,我使用的是 Spock-0.7-groovy-2.0.jar
-
您断言“必须修改代码才能使此 [测试] 工作真的非常糟糕。”。那要看。做好 TDD/BDD 通常需要代码设计良好且模块化以进行有效测试,尤其是如果您想使用 mock 和 stub 进行测试。因此,如果一个测试框架促使您使您的代码更加模块化或可配置,那么它可能被认为是“非常非常好的”。要求将协作者作为构造函数参数传递而不是硬编码可能适合该类别。查看优秀的 GOOS 书籍 - 通过测试发展面向对象的软件。
-
你的说法没问题,我的断言(方式)太强了。尽管如此,在这种情况下,我仍然觉得仅仅为了帮助测试框架识别正确的模拟方法而需要更改代码是不行的......