2017-07-25 51 views
-1

我想定義一個抽象基類來定義通用可重用測試用例,我希望在測試各種API時一遍又一遍地執行這些測試用例。這個想法是定義一次測試,然後我可以從通用測試繼承來獲得測試定義,我所有的測試代碼都要執行測試執行的具體細節。這樣我就不會錯過好的測試用例,我不需要重新發明輪子,也不需要在數十個地方進行更改,當我想要另一個好的測試用例進行測試時。從調用堆棧中檢索最頂級註釋的函數

實質上,我正在做參數化測試,其中的參數是在基類中定義的。預期的結果也在基類中定義。但是,有時需要覆蓋預期的結果。例如,普通的字符串字段可能允許任何字符,而對象名字段可能(或者在我的情況下)限制哪些字符是合法的。例如,一個字符串字段允許「?」在該值中,名稱字段沒有。

我想弄清楚在基類中定義參數ONCE的乾淨方式,並且只定義了一次預期的結果,但是在上下文需要它的實現類中覆蓋它。

我的問題是,當你重寫一個方法時,你必須替換它的實現。你不能只替換它的一部分。我想在不覆蓋參數部分的情況下替換預期的結果。我也不認爲它是一種乾淨或理想的解決方案,可以將每個測試分解爲兩種方法,一種提供參數,另一種定義預期行爲。

我正在考慮的一個選項是使用註釋來定義預期結果。然後我可以重寫註釋,然後將實現委託給基類實現。只要基類使用註釋來決定如何表現,它就應該起作用。

例如:(僞代碼,而不是真正的Java)

abstract class foo { 

    @fubar(true) 
    void test1() { doSomething(param1) } 

    @fubar(true) 
    void test2() { doSomething(param2) } 

    @fubar(false) 
    void test3() { doSomething(param3) } 

    boolean isFubar(void) { ... } // generic annotation grabber 

    void doSomething(p) { 
    if (isFubar()) 
     doThis(p) 
    else 
     doThat(p); 
    } 

    // abstract methods to be defined in the implementing class 
    void doThis(p); 
    void doThat(p); 
} 

class bar extends foo { 

    // change the expected result for test2 only 
    @fubar(false) 
    void test2() { super.test2(); } 

    // define how to execute the tests 
    void doThis(p) { ... } 
    void doThat(p) { ... } 

} 

class baz extends bar { 

    // only changes the semantics of test3, nothing else 
    @fubar(true) 
    void test3() { super.test3() } 

} 

考慮到這種層次,foo.test1()bar.test1()baz.test1()都做同樣的事情。 foo.test2()做一件事,而bar.test2()baz.test2()做別的事情。同樣,foo.test3()bar.test3()做了一件事,但baz.test3()會有所不同。

我可以使用註釋來完成此行爲嗎?如果是這樣,isFubar會是什麼樣子?我還沒有看到方法名稱未知的這種反射的例子。

作爲一個側面說明,如果沒有完成預期的行爲更清潔的方式,我很樂意聽到的是什麼。

+1

聞起來像一個可能的XY問題。您可能希望查看AOP,或嘗試找到更多面向對象的方法。 – shmosel

+1

請詳細說明*你想要完成什麼,而不是*如何*。按照你的代碼片斷,簡單地重寫'isFubar()'返回true或false,並完全丟棄註釋會更有意義。 – shmosel

+1

此外,您提出的問題是作爲代碼請求發生的,這是在此處皺起眉頭。你有沒有試圖解決這個問題?你卡在哪裏? – shmosel

回答

0

要查找調用堆棧最早匹配註釋,你可以使用這樣的函數:

static <T extends Annotation> T seekAnnotation(Class<T> annotationClass) { 
    T annotation = null; 
    try { 
     for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { 
      T found = seekAnnotation(annotationClass, ste); 
      if (found != null) { 
       annotation = found; 
      } 
     } 
    } catch (Exception e) { 
     // 
    } 
    return annotation; 
} 

static <T extends Annotation> T seekAnnotation(Class<T> annotationClass, StackTraceElement ste) { 
    T annotation = null; 
    try { 
     Class<?> claz = Class.forName(ste.getClassName()); 
     Method method = claz.getDeclaredMethod(ste.getMethodName()); 
     annotation = method.getAnnotation(annotationClass); 
    } catch (Exception e) { 
     // 
    } 
    return annotation; 
} 

還有更多的工作,使之堅固,但是這將是基本的想法。所以,你的方法isFubar()會是什麼樣子

boolean isFubar() { 
    return seekAnnotation(fubar.class).value(); 
} 
+0

不幸的是,在這個例子中事物過於簡單。無論哪種方法都有註釋,「isFubar」的定義需要工作。因爲在課堂上可能有50-100個有註釋的方法。這就是我提到遍歷調用堆棧的原因。 –

+0

這裏是第二個版本,走棧跟蹤。 –

+0

不錯!它看起來像,除非它在循環時出錯,它將採用調用堆棧中最高的註釋值,這正是我想要的。它能夠做到這一點,而不必知道具體的方法名稱,這是我不清楚如何做。我將把這標記爲我的問題的答案。 –