如何验证是否在 Spock 中调用了 super class 方法?
How to verify whether a super class method was called in Spock?
假设我有以下 class 结构:
class Parent {
public void method() {
// Some calculations
}
}
class Child extends Parent {
@Override
public void method() {
super.method();
// Some additional logic
}
}
我正在对 Child.method
进行 spock 测试,并想验证是否从 Child.method
调用了 Parent.method
。我做了一些研究,但没有找到任何令人满意的解决方案来解决我的问题。
如何在 Spock 测试中验证在 Child.method
的调用中也调用了 superclass 方法 (Parent.method
)?
已知解决方案:在 Child
中将 super.method()
移动到单独的包私有方法。
想知道有没有更好的解决办法
我从未使用过 Spock 框架,但我认为您可以使用运算符实例或反射来检查 Parent.method 中实例的类型。
tim_yates评论:
Why do you want to test this? Can't you tell as the super class calculations were performed?
我完全同意。我不会对此进行测试,因为正如 @Override
所暗示的那样,契约是一种覆盖,对超级 class 方法的委托是可选的。你为什么要强迫你的用户调用 super class 方法?但正如蒂姆所说,您可以测试对您很重要的副作用。这是一个小例子,一个副作用是字段赋值,另一个是写入 System.out
的东西(可能很傻,但只是为了用模拟来展示一些不明显的东西):
package de.scrum_master.Whosebug.q60167623;
public class Parent {
protected String name;
public void method() {
// Some calculations
System.out.println("parent method");
name = "John Doe";
}
}
package de.scrum_master.Whosebug.q60167623;
class Child extends Parent {
@Override
public void method() {
super.method();
// Some additional logic
System.out.println("child method");
}
public static void main(String[] args) {
new Child().method();
}
}
package de.scrum_master.Whosebug.q60167623
import spock.lang.Specification
class ChildTest extends Specification {
static final PrintStream originalSysOut = System.out
PrintStream mockSysOut = Mock()
def setup() {
System.out = mockSysOut
}
def cleanup() {
System.out = originalSysOut
}
def test() {
given:
def child = new Child()
when:
child.method()
then:
1 * mockSysOut.println({ it.contains("parent") })
child.name == "John Doe"
}
}
更新: 你想做的事情在技术上是不可能的,原因是:它会破坏封装,见 here, here, indirectly also here。该方法是覆盖,这个词说明了一切。测试(副作用)或方法的结果,而不是它的交互(它实际上被调用)。 Spock 的交互测试功能被过度使用,尽管 Spock 手册在某些地方警告过规范。它只会让你的测试变得脆弱。交互测试适用于像 publish/subscribe(观察者模式)这样的设计模式,在这种情况下测试对象之间的交互是有意义的。
如果您需要强制调用 Parent
中的某些功能,您应该通过设计而不是测试来强制执行。
abstract class Parent {
public final void method() {
// Some calculations
additionalLogic();
}
protected abstract void additionalLogic();
}
class Child extends Parent {
@Override
protected void additionalLogic() {
super.method();
// Some additional logic
}
}
您当然可以不这样做 abstract
,只需为 additionalLogic()
添加一个空操作实现即可。
tim_yates 和 kriegaex 是丛林中的大野兽,当谈到好的和坏的 Spock 或一般的 TDD 式测试时......他们不止一次(正确地)在他们这里的做法,基本上是基于测试代码而不是实现。
虽然有时很难。也许在某些情况下您想要测试 super.doSomething()
的调用。我只是使用 TDD 将已经完成 "spike" 的编辑器放在一起,其中我没有测试就匆匆忙忙地完成了 TreeTableView
的编辑器。 "spike"可见here。在对我的回答的建设性评论中,kleopatra 建议我检查(即在应用程序代码中放置一个 if
)以确保 super.startEdit()
确实在继续之前开始编辑单元格,所以在在这种情况下,将 super.startEdit()
的 "side-effect" 测试为 isEditing()
现在 returns true
是不够的。你真的需要知道你的 class 的 startEdit()
实际上只是调用 super.startEdit()
.
但是,我不相信这是可以做到的,而且 tim_yates 或 kriegaex 几乎肯定会说如果可能的话你是怎么做到的。
因此,我建议的 TDD 解决方案如下所示:
def 'super start edit should be called if cell is not empty'(){
given:
// NB has to be GroovySpy because isEmpty() is final
DueDateEditor editor = GroovySpy( DueDateEditor ){
isEmpty() >> false
}
when:
editor.startEdit()
then:
1 * editor.callSuperStartEdit()
}
class DueDateEditor extends TreeTableCell {
@Override
void startEdit(){
if( ! isEmpty() ) {
// this is the line you have to add to make the test pass
callSuperStartEdit()
}
}
def callSuperStartEdit(){
super.startEdit()
}
}
我认为你必须 "spawn" 人工的单一用途方法,因为准确地说,根本没有副作用!
PS 实际上,我会将此测试参数化,以便它在第二次调用中 returns true
到 isEmpty()
,并要求不调用该方法那种情况。
假设我有以下 class 结构:
class Parent {
public void method() {
// Some calculations
}
}
class Child extends Parent {
@Override
public void method() {
super.method();
// Some additional logic
}
}
我正在对 Child.method
进行 spock 测试,并想验证是否从 Child.method
调用了 Parent.method
。我做了一些研究,但没有找到任何令人满意的解决方案来解决我的问题。
如何在 Spock 测试中验证在 Child.method
的调用中也调用了 superclass 方法 (Parent.method
)?
已知解决方案:在 Child
中将 super.method()
移动到单独的包私有方法。
想知道有没有更好的解决办法
我从未使用过 Spock 框架,但我认为您可以使用运算符实例或反射来检查 Parent.method 中实例的类型。
tim_yates评论:
Why do you want to test this? Can't you tell as the super class calculations were performed?
我完全同意。我不会对此进行测试,因为正如 @Override
所暗示的那样,契约是一种覆盖,对超级 class 方法的委托是可选的。你为什么要强迫你的用户调用 super class 方法?但正如蒂姆所说,您可以测试对您很重要的副作用。这是一个小例子,一个副作用是字段赋值,另一个是写入 System.out
的东西(可能很傻,但只是为了用模拟来展示一些不明显的东西):
package de.scrum_master.Whosebug.q60167623;
public class Parent {
protected String name;
public void method() {
// Some calculations
System.out.println("parent method");
name = "John Doe";
}
}
package de.scrum_master.Whosebug.q60167623;
class Child extends Parent {
@Override
public void method() {
super.method();
// Some additional logic
System.out.println("child method");
}
public static void main(String[] args) {
new Child().method();
}
}
package de.scrum_master.Whosebug.q60167623
import spock.lang.Specification
class ChildTest extends Specification {
static final PrintStream originalSysOut = System.out
PrintStream mockSysOut = Mock()
def setup() {
System.out = mockSysOut
}
def cleanup() {
System.out = originalSysOut
}
def test() {
given:
def child = new Child()
when:
child.method()
then:
1 * mockSysOut.println({ it.contains("parent") })
child.name == "John Doe"
}
}
更新: 你想做的事情在技术上是不可能的,原因是:它会破坏封装,见 here, here, indirectly also here。该方法是覆盖,这个词说明了一切。测试(副作用)或方法的结果,而不是它的交互(它实际上被调用)。 Spock 的交互测试功能被过度使用,尽管 Spock 手册在某些地方警告过规范。它只会让你的测试变得脆弱。交互测试适用于像 publish/subscribe(观察者模式)这样的设计模式,在这种情况下测试对象之间的交互是有意义的。
如果您需要强制调用 Parent
中的某些功能,您应该通过设计而不是测试来强制执行。
abstract class Parent {
public final void method() {
// Some calculations
additionalLogic();
}
protected abstract void additionalLogic();
}
class Child extends Parent {
@Override
protected void additionalLogic() {
super.method();
// Some additional logic
}
}
您当然可以不这样做 abstract
,只需为 additionalLogic()
添加一个空操作实现即可。
tim_yates 和 kriegaex 是丛林中的大野兽,当谈到好的和坏的 Spock 或一般的 TDD 式测试时......他们不止一次(正确地)在他们这里的做法,基本上是基于测试代码而不是实现。
虽然有时很难。也许在某些情况下您想要测试 super.doSomething()
的调用。我只是使用 TDD 将已经完成 "spike" 的编辑器放在一起,其中我没有测试就匆匆忙忙地完成了 TreeTableView
的编辑器。 "spike"可见here。在对我的回答的建设性评论中,kleopatra 建议我检查(即在应用程序代码中放置一个 if
)以确保 super.startEdit()
确实在继续之前开始编辑单元格,所以在在这种情况下,将 super.startEdit()
的 "side-effect" 测试为 isEditing()
现在 returns true
是不够的。你真的需要知道你的 class 的 startEdit()
实际上只是调用 super.startEdit()
.
但是,我不相信这是可以做到的,而且 tim_yates 或 kriegaex 几乎肯定会说如果可能的话你是怎么做到的。
因此,我建议的 TDD 解决方案如下所示:
def 'super start edit should be called if cell is not empty'(){
given:
// NB has to be GroovySpy because isEmpty() is final
DueDateEditor editor = GroovySpy( DueDateEditor ){
isEmpty() >> false
}
when:
editor.startEdit()
then:
1 * editor.callSuperStartEdit()
}
class DueDateEditor extends TreeTableCell {
@Override
void startEdit(){
if( ! isEmpty() ) {
// this is the line you have to add to make the test pass
callSuperStartEdit()
}
}
def callSuperStartEdit(){
super.startEdit()
}
}
我认为你必须 "spawn" 人工的单一用途方法,因为准确地说,根本没有副作用!
PS 实际上,我会将此测试参数化,以便它在第二次调用中 returns true
到 isEmpty()
,并要求不调用该方法那种情况。