2016-09-21 38 views
4

在用Java編寫的大中型開源項目中,我們收到許多錯誤報告,針對這些錯誤報告的單元測試更少,以及更少的關閉這些錯誤的補丁。瞭解@ Ignore'd測試何時傳遞

當提供單元測試但未提供補丁時,我們通過將測試功能添加到套件來驗證是否存在針對幹線的缺陷。我們承諾它,但添加一個註釋,以免打破已知bug的構建。

一些錯誤可能是相關的,或者對一個錯誤所做的更改可能會修復另一個錯誤。我們想知道以前已知失敗的測試用例何時不再失敗,因此我們可以通知觀看該錯誤並關閉錯誤的人員。

比方說,我們有一些越野車功能 - 引發異常,有意外的副作用,或返回錯誤的值。 (Real world example

public void buggyFunction() { 
    throw new UnsupportedOperationException("this will be implemented later"); 
} 

而一些單元測試,以測試buggyFunction

@Test 
public void knownIssue() { 
    buggyFunction(); 
} 

選項1@Ignore測試

@Ignore("this test is currently failing. see bug 12345") 
@Test 
public void knownIssue() { 
    buggyFunction(); 
} 

選項2:標準junit4方式是使用@Test(expected=MyException.class)或撒上@RuleExpectedException s整個測試功能。也不要給用戶一個有用的信息,說明爲什麼一個失敗的測試意味着一個錯誤已經修復,並且更新單元測試並關閉錯誤。另外,如果拋出期望的異常,則測試通過,但在這種情況下測試沒有意義。如果失敗(錯誤修復時)或跳過(當錯誤仍然打開時)會更好。

// when this bug is fixed, it should not throw an exception 
// TODO: delete expected=UnsupportedOperationException.class 
@Test(expected=UnsupportedOperationException.class) 
public void knownIssue() { 
    buggyFunction(); 
} 

OR

@Rule 
public final ExpectedException thrown = ExpectedException.none(); 

@Test 
public void knownIssue() { 
    thrown.expect(UnsupportedOperationException.class); 
    thrown.expectMessage("this will be implemented later"); 
    buggyFunction(); 
    thrown.expect(ExpectedException.none()); 
} 

選項3:布格了鍋爐板腳手架測試

@Test 
public void knownIssue() { 
    try { 
     buggyFunction(); 
    } catch (UnsupportedOperationException e) { 
     // we know that buggyFunction is broken, so skip this test 
     assumeTrue("Skipping test. Expected exception: " + e, false); 
    } 
    // surprise! buggyFunction isn't broken anymore! 
    fail("The function is no longer buggy! " + 
     "Update the unit test and close bug 12345!"); 
} 

是否有沒有更好的方法是:

  • 做當已知的問題是開放
  • 通知我們,當一個已知的問題是
  • 優選的是,作爲已知的問題是開放的,只要算作一個跳過測試
  • 最好,亂the-不破壞構建從Hamcrest或其他庫的框解決方案

我可以在Python中非常容易地完成這樣的事情,其中​​未評估函數是第一類對象。在Java 6中也可以做到這一點(是的,這是我們正在使用的版本),但可能需要比選項3更多的樣板。請告訴我我錯了。

def alertWhenFixed(expected=Exception, bug=12345): 
    def decorator(func): 
     def func_wrapper(*args, **kwargs): 
      try: 
       func(*args, **kwargs) 
      except Exception as e: 
       if isinstance(e, expected): 
        assumeTrue("Skipping test. Expected exception: {}" 
           .format(e), false) 
       else: 
        raise e 
      fail("The function is no longer buggy! " + 
       "Update the unit test and close bug {}".format(bug)) 
     return func_wrapper 
    return decorator 

@alertWhenFixed(expected=UnsupportedOperationException, bug=12345) 
def knownIssue(): 
    buggyFunctionThrowsException() 

@alertWhenFixed(expected=AssertionFailed) 
def knownIssue(): 
    assertEquals(42, buggyFunctionReturnsWrongValue()) 

@alertWhenFixed(expected=AssertionFailed) 
def knownIssue(): 
    buggyFunctionHasWrongSideEffect() 
    assertEquals(42, getSideEffect()) 

此修飾器可以測試引發異常,返回錯誤值或產生錯誤副作用的已知問題。

這個裝飾器是100%可重用的,所以沒有複製麪食嘗試/除了腳手架,我可以刪除一行代碼,當已知問題已修復時,最重要的是我可以離開測試案例邏輯。

任何想法,如果這可以被翻譯成Java 6或7?

+0

以我的經驗,最好的方法是不要放棄忽略,不要指望Exception,不要添加try catch,而是要解決實際問題。 – Stultuske

+2

@Stultuske這是一個很好的主意,顯然每個人都會同意。但在現實世界中,特別是在大型組織和大型項目中,事情並不那麼簡單。或者你真的認爲他會寫這個冗長的問題,如果他會談論他可以在此期間修復的兩個錯誤?! – GhostCat

+0

@GhostCat你的意思是你認爲將產品投入生產是可以接受的,因爲知道其中存在缺陷,因爲你有可能忽略測試嗎? – Stultuske

回答

2

除了您自己勾畫的選項之外,我沒有看到其他技術選項。

我認爲解決方案來自不同的角度:你的工具不給開發人員他們需要的反饋。你看,重點是:當開發者決定修復buggyFunction()那麼這需要嚴格組織的方式發生。含義:

開發商想要修復的一個錯誤。因此,他應該充分認識到錯誤的,和所有涉及到這項任務的工作:

  • 在你的bug跟蹤系統進行更新
  • 更重要的是:運行所有測試!

換句話說:您希望您的開發人員收到快速反饋。他應該可以更改buggyFunction,然後幾分鐘後他應該通知現在失敗的測試用例。

所以即使他偶然修復了這個bug,關鍵是他通知了他的變化在別處打破了考驗。然後,他的責任是什麼,其他測試打破了。如果你的系統不能有效地支持這個工作流程,那麼改變你的單元測試根本無濟於事。因爲你的單元測試是而不是這裏真正的問題。

從這個角度來看,唯一的選擇是在選項2和3之間。因爲它們給你「最好」的系統這部分可以給你的東西:有人更改代碼,然後測試中斷。儘快通知更改代碼的人;然後確定發生了什麼。從那裏開始,這隻關乎測試的質量(指出測試失敗時意味着什麼);和所涉人員的技能(然後做正確的事情)。

3

創建一個單獨的TestSuite錯誤識別器,只有當錯誤出現(!)時纔會通過。

/** 
* @see ...original now ignored test 
*/ 
@Test 
public void defect123124() { 
    expectThrows(UnsupportedOperationException.class,() -> buggyFunction()); 
} 

當測試「失敗」時,表示您可以刪除它並取消缺陷。 當然,常規測試套件中需要進行反向測試。有@Ignore刪除。

現在你有一個很好的概述。另外,程序員修復了一些不同的東西,並且突然失敗的錯誤測試套件獲得了他的獎金。

1

如果您正在使用Testsuites或使用它們是您的一個選項,您可以嘗試使用JUnit Category註釋。 (或者取決於你的構建系統,你甚至可以在沒有測試套件的情況下做到。查找鏈接文章的更多細節。)

因此,你需要創建一個標記接口:

public interface KnowIssue { 
    // JUnit category marker interface 
} 

代替將@Ignore註釋您應用類別發生故障的測試方法(甚至是整個測試類)和:

@Test 
@Category(KnowIssue.class) 
public void myFailingTest() { 
    // some test 
} 

然後在套件(S)爲您的綠色測試排除了KnownIssue類別:

@RunWith(Categories.class) 
@ExcludeCategory(KnownIssue.class) // <- exclude known issues! 
@Suite.SuiteClasses({ /** your Testclasses here */ }) 
public class AllMyGoodTests { 
    // test suite 
} 

要檢查你的已知問題創建第二個測試套件,你欠幅帽子(regularily),看看你的任何已知問題得到固定的(綠色)的事件:

@RunWith(Categories.class) 
@IncludeCategory(KnownIssue.class) // <- only run known issues! 
@Suite.SuiteClasses({ /** your Testclasses here */ }) 
public class KnownIssueTests { 
    // test suite 
} 

然後移除該測試的類別,它會再次自動執行您的「良好」測試。

甚至可能用自定義test runner@RunWith(KnownIssueCategoryRunner.class)反轉測試結果(紅色< - >綠色),但我從來沒有真正嘗試過。