Spock Unroll似乎用boolean参数打印出一些奇怪的东西 [英] Spock Unroll seems to print something odd with boolean parameter
问题描述
我只是将这种测试方法放在一起:
I just put this test method together:
@Unroll
def 'super start edit should be called if cell is not empty'( boolean empty ){
given:
DueDateEditor editor = GroovySpy( DueDateEditor ){
isEmpty() >> empty
}
when:
editor.startEdit()
then:
if( empty){
0 * editor.callSuperStartEdit()
}
else {
1 * editor.callSuperStartEdit()
}
where:
empty | _
true | _
false | _
}
...就通过的两个测试而言,它工作正常……但是,当它失败时,这很奇怪:如果参数empty
为false
的输出为
... it works OK in terms of the two tests passing... but when you make it fail it's very odd: the output if the parameter empty
is false
is
super start edit should be called if cell is not empty[1]
...,如果参数empty
为true
,则为0.这是一个错误吗?
... and it is 0 if the parameter empty
is true
. Is this a bug?
推荐答案
我正在写一个附加答案,因为
I am writing an additional answer because
- Tim关于标题的解决方案中有一个小错误(但从技术上来说,他的回答仍然是绝对正确的!),
- 这里不需要
GroovySpy
,一个简单的Spy
就足够了, - 我想向您展示另一种测试方法,而无需添加
isEmpty()
, - 我想向您展示如何使用三元表达式中的调用次数进行一次交互,而不是if-else(即使错误报告很丑陋),
- 我想评论一下您的总体测试方式(请参阅本文结尾).
- there is a small bug in Tim's solutions with regard to the title (but still his answer is technically absolutely correct!),
- you don't need a
GroovySpy
here, a simpleSpy
absolutely suffices, - I want to show you an alternative way of testing without stubbing
isEmpty()
, - I want to show you how you can use just one interaction with the number of calls in a ternary expression instead of an if-else (even though error reporting is ugly then),
- I want to comment on your way of testing in general (see the end of this post).
package de.scrum_master.stackoverflow.q61032514;
import java.time.LocalDate;
public class DueDateEditor {
String text;
public boolean isEmpty() {
return text == null || text.trim() == "";
}
public void startEdit() {
if (!isEmpty())
callSuperStartEdit();
}
public void callSuperStartEdit() {}
}
package de.scrum_master.stackoverflow.q61032514
import spock.lang.Specification
import spock.lang.Unroll
class DueDateEditorTest extends Specification {
@Unroll
def 'super start edit #shouldMsg be called if the cell is #cellStateMsg'() {
given:
DueDateEditor editor = Spy() {
isEmpty() >> empty
}
when:
editor.startEdit()
then:
(empty ? 0 : 1) * editor.callSuperStartEdit()
where:
empty << [true, false]
shouldMsg = empty ? 'should not' : 'should'
cellStateMsg = empty ? 'empty' : 'not empty'
}
@Unroll
def "super start edit #shouldMsg be called if cell text is '#text'"() {
given:
DueDateEditor editor = Spy()
editor.text = text
when:
editor.startEdit()
then:
(editor.isEmpty() ? 0 : 1) * editor.callSuperStartEdit()
// Or, if 'isEmpty()' has a side effect:
// (text ? 1 : 0) * editor.callSuperStartEdit()
where:
text << ["foo", "", null, "line 1\nline 2"]
shouldMsg = text ? 'should' : 'should not'
cellStateMsg = text ? 'not empty' : 'empty'
}
}
一般说明:
- 我不会通过交互来测试单个类的内部布线.该测试将很脆弱,如果您在内部重构该类而根本不更改API,则如果交互不再如预期那样,则测试可能会中断.我认为这过于规范,我只会对不同类之间或一个类的不同实例之间的关键交互使用交互测试,关键"意味着诸如Observer之类的设计模式的功能.
- 如果整个测试仅确切知道这两种情况,则可以通过两种不同的交互模式来区分两种情况,这会使测试的可读性和复杂性降低,请参阅您自己的代码以及我的代码和Tim的代码.在这种情况下,我宁愿编写两个具有简单标题和简单功能但没有if-else或三元表达式,没有标题等辅助变量的特征方法.
P.S .:对不起,我必须组成一个正在测试DueDateEditor
的示例类,以使我的测试能够按预期进行编译和运行.像往常一样,不幸的是,迈克没有提供 MCVE ,而只是提供了一部分.
P.S.: Sorry, I had to make up a sample class under test DueDateEditor
in order to make my test compile and run as expected. As usual, Mike unfortunately didn't provide an MCVE but just a part of it.
更新:我们在评论中谈到了GroovySpy
,正如我所说,如果您的类是Java类,并且您想存根一个最终方法,那将是行不通的,请参见 Spock手册.这是给您的证明:
Update: We talked about GroovySpy
in our comments and, as I said, it will not work if your classes are Java classes and there is a final method in you want to stub, see the Spock manual. Here is proof for you:
package de.scrum_master.stackoverflow.q61032514;
public class TreeTableCell<A, B> {
String text;
public final boolean isEmpty() {
return text == null || text.trim() == "";
}
}
package de.scrum_master.stackoverflow.q61032514;
import java.time.LocalDate;
public class DueDateEditor extends TreeTableCell<String, LocalDate> {
public void startEdit() {
if (!isEmpty())
callSuperStartEdit();
}
public void callSuperStartEdit() {}
}
package de.scrum_master.stackoverflow.q61032514
import spock.lang.Specification
import spock.lang.Unroll
class DueDateEditorTest extends Specification {
@Unroll
def 'super start edit #shouldMsg be called if the cell is #cellStateMsg'() {
given:
DueDateEditor editor = GroovySpy() {
isEmpty() >> empty
}
when:
editor.startEdit()
then:
(empty ? 0 : 1) * editor.callSuperStartEdit()
where:
empty << [true, false]
shouldMsg = empty ? 'should not' : 'should'
cellStateMsg = empty ? 'empty' : 'not empty'
}
}
如果您的应用程序类仅是Groovy类,则该测试将起作用.但是,如果它们像我的示例一样是Java类,则测试将失败,如下所示:
The test would work if your application classes were Groovy classes only. But if they are Java classes like in my example, the test will fail like this:
Too few invocations for:
(empty ? 0 : 1) * editor.callSuperStartEdit() (0 invocations)
Unmatched invocations (ordered by similarity):
1 * editor.startEdit()
methodName == "callSuperStartEdit"
| |
startEdit false
10 differences (44% similarity)
(s---------)tartEdit
(callSuperS)tartEdit
因此,在这种情况下,您不能仅使用Groovy魔术来检查交互.但是正如我所说,您无论如何都不应该这样做.而是要确保startEdit()
和callSuperStartEdit()
都做正确的事情.检查其结果,或者如果为void
,请检查其对被测对象或其合作者的状态的副作用.
So in this case you cannot just use Groovy magic to check interactions. But as I said, you shouldn't do that anyway. Rather make sure that both startEdit()
and callSuperStartEdit()
do the right things. Check their results or, if they are void
, check their side effects on the state of the subject under test or its collaborators.
更新2:关于您最初关于索引方法命名的问题,实际上 @tim_yates 给出了正确的答案.我只想添加相应的Spock手册链接,说明方法展开,如何使用where:
块中的变量影响命名.
Update 2: Regarding your original question about indexed method naming, actually @tim_yates gave the correct answer. I just want to add the corresponding Spock manual link explaining method unrolling and how you can influence naming using variables from the where:
block.
这篇关于Spock Unroll似乎用boolean参数打印出一些奇怪的东西的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!