有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何验证在Spock中是否调用了超类方法?

想象一下,我的班级结构如下:

class Parent {
  public void method() {
    // Some calculations
  }
}
class Child extends Parent {
  @Override
  public void method() {
    super.method();
    // Some additional logic
  }
}

我正在测试Child.method,想验证是否从Child.method调用了Parent.method。我做了一些研究,但没有找到任何令人满意的解决方案

如何在Spock测试中验证在调用Child.method时也调用了超类方法(Parent.method

已知解决方案:在Child中,将super.method()移动到一个单独的包私有方法

我想知道是否有更好的解决办法


共 (4) 个答案

  1. # 1 楼答案

    tim_yates和kriegaex是丛林中的大野兽,当涉及到好的和坏的Spock,或TDD风格的测试时。。。他们不止一次(正确地)以他们在这里所做的方式来区分我的问题,基本上是基于测试代码而不是实现

    但有时候很难。也许在某些情况下,您需要测试super.doSomething()的调用。我只是用TDD把一个TreeTableView的编辑整合起来,已经完成了一个“spike”,在这个“spike”中我没有测试就冲到了前面。可以看到“尖峰”。在对我的回答的建设性评论中,kleopatra建议我检查(即在应用程序代码中添加一个if),以确保super.startEdit()在继续之前确实已经开始编辑单元格,因此在这种情况下,不足以测试super.startEdit()的“副作用”,因为isEditing()现在返回true。你真的需要知道你的类的startEdit()实际上只是调用super.startEdit()

    然而,我不相信这是可以做到的,蒂姆·耶茨或克里加克斯几乎肯定会说,如果可能的话,你是如何做到的

    因此,我建议的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()
        }
    }
    

    我认为你必须“繁殖”一种人工单一用途的方法,因为准确地说,根本没有副作用

    实际上,我将参数化这个测试,以便它在第二次调用中返回trueisEmpty(),并要求在这种情况下不调用该方法

  2. # 2 楼答案

    如果需要强制调用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()添加一个无操作实现

  3. # 3 楼答案

    蒂姆·耶茨评论道:

    Why do you want to test this? Can't you tell as the super class calculations were performed?

    我完全同意。我不会对此进行测试,因为正如@Override所暗示的,契约是一种重写,对超类方法的委托是可选的。为什么要强迫用户调用超级类方法?但正如蒂姆所说,你可以测试对你来说很重要的副作用。下面是一个小例子,一个副作用是字段赋值,另一个副作用是写入System.out的内容(可能很傻,但只是为了用模拟显示一些不明显的内容):

    package de.scrum_master.stackoverflow.q60167623;
    
    public class Parent {
      protected String name;
    
      public void method() {
        // Some calculations
        System.out.println("parent method");
        name = "John Doe";
      }
    }
    
    package de.scrum_master.stackoverflow.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.stackoverflow.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"
      }
    }
    

    更新:从技术上讲,您想要做的事情是不可能的,原因是:它会破坏封装,请参见herehere,也可以间接地here。方法被覆盖,这个词说明了一切。测试一个方法的(副作用)或结果,而不是它的交互作用(它实际上被调用)。Spock的交互测试功能被过度使用,尽管Spock手册警告某些地方存在过度规范。这只会让你的测试变得脆弱。交互测试适用于发布/订阅(观察者模式)等设计模式,在这些模式中,测试对象之间的交互是有意义的

  4. # 4 楼答案

    我从未使用过Spock框架,但我认为您可以在父类中检查实例的类型。具有运算符或反射实例的方法