簡體   English   中英

如何驗證Spock中是否調用了超類方法?

[英]How to verify whether a super class method was called in Spock?

想象一下,我有以下類結構:

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

我正在對Child.method spock 測試,並想驗證Parent.method是否是從Child.method調用的。 我做了一些研究,但沒有找到任何令人滿意的解決方案來解決我的問題。

如何在 Spock 測試中驗證在調用Child.method調用了超類方法( Parent.method )?

已知解決方案:在Child中將super.method()移動到單獨的包私有方法。

我想知道是否有更好的解決方案。

tim_yates評論道:

你為什么要測試這個? 執行超類計算時您看不出來嗎?

我完全同意。 我不會對此進行測試,因為正如@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"
  }
}

更新:您想要做的事情在技術上是不可能的,原因是:它會破壞封裝,請參閱此處此處,間接也此處 該方法被覆蓋,這個詞說明了一切。 測試方法的(副作用)或結果,而不是它的交互(它實際上被調用)。 盡管 Spock 手冊在某些地方警告過度規范,但 Spock 的交互測試功能被過度使用。 它只會讓你的測試變得脆弱。 交互測試適用於像發布/訂閱(觀察者模式)這樣的設計模式,在這種模式下測試對象之間的交互是有意義的。

如果您需要強制調用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 組合在一起,已經完成了一個“尖峰”,我在沒有測試的情況下沖進了一個TreeTableView的編輯器。 這里可以看到“尖峰”。 在對我的回答的建設性評論中,kleopatra 建議我檢查(即在應用程序代碼中放置一個if )以確保super.startEdit()在繼續之前確實開始編輯單元格,所以在這種情況下它是不足以測試super.startEdit()的“副作用”,因為isEditing()現在返回true 你真的需要知道你的類的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()
    }
}

我認為您必須“產生”一種人工的單一用途方法,因為完全沒有副作用!

PS 我實際上會參數化這個測試,以便它在第二次調用中返回trueisEmpty() ,並要求在這種情況下不調用該方法。

我從未使用過 Spock 框架,但我認為您可以使用運算符或反射的實例檢查 Parent.method 中的實例類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM