2014-01-07 169 views
2

我正在爲某些用戶數據實現驗證器。如何避免重複的JUnit測試

不幸的是,用戶數據總共包含37個字段,這使得測試類相當怪異。

大多數字段包含具有相似要求的數據(即檢查長度限制)。

摘錄代碼:

public class Userdata { 

    private String   firstName; 
    private String   lastName; 
    private String   birthName; 
    private String   birthPlace; 
    private String   city; 
    private String   street; 
    private String   zipCode; 
    private String   country; 
    private String   citizenship; 

    // getters/setters 
} 

和(部分)測試類:

public class UserdataValidatorTest { 
    UserdataValidator cut = new UserdataValidator(); 

    @Test 
    public void firstNameMaxPermittedLength() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setFirstName(utf8Chars(64)); 

     assertNoViolations(cut.validate(userdata)); 
    } 

    @Test 
    public void firstNameExceedsPermittedLength() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setFirstName(utf8Chars(65)); 

     assertSingleViolation(cut.validate(userdata)); 
    } 

    @Test 
    public void lastNameMaxPermittedLength() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setLastName(utf8Chars(64)); 

     assertNoViolations(cut.validate(userdata)); 
    } 

    @Test 
    public void lastNameExceedsPermittedLength() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setLastName(utf8Chars(65)); 

     assertSingleViolation(cut.validate(userdata)); 
    } 

    @Test 
    public void citizenshipIllegalChars() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setCitizenship("2E"); 

     assertSingleViolation(cut.validate(userdata)); 
    } 

    @Test 
    public void citizenshipLegal() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setCitizenship(iso3166Country()); 

     assertNoViolations(cut.validate(userdata)); 
    } 

    @Test 
    public void citizenshipTooLong() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setCitizenship(alphabetic(3)); 

     assertSingleViolation(cut.validate(userdata)); 
    } 

    @Test 
    public void citizenshipTooShort() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setCitizenship(alphabetic(1)); 

     assertSingleViolation(cut.validate(userdata)); 
    } 

    @Test 
    public void countryIllegalChars() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setCountry("2E"); 

     assertSingleViolation(cut.validate(userdata)); 
    } 

    @Test 
    public void countryLegal() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setCountry(iso3166Country()); 

     assertNoViolations(cut.validate(userdata)); 
    } 

    @Test 
    public void countryTooLong() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setCountry(alphabetic(3)); 

     assertSingleViolation(cut.validate(userdata)); 
    } 

    @Test 
    public void countryTooShort() 
    { 
     Userdata userdata = minimumRequirements(); 
     userdata.setCountry(alphabetic(1)); 

     assertSingleViolation(cut.validate(userdata)); 
    } 

    // ... more tests 
} 

有沒有生成測試用例一個愉快的方式?我仍然想爲每個國家進行4次測試。

編輯

有許多複雜的(互相依賴)領域,這是我在這裏省略,這是一個原因,我決定不使用Bean驗證API。

+0

那麼在這些驗證器的循環中拋出很多隨機數據(使用常量種子;請參閱Random(int seed))?生成的數據+循環=不那麼詳細。 – tucuxi

+1

另請參閱http://stackoverflow.com/questions/6453235/what-does-damp-not-dry-mean-when-talking-about-unit-tests – Raedwald

回答

2

恕我直言,單元測試不是生產代碼,不必拘泥於同一標準。您可以輕鬆地重構和刪除測試代碼,而不會直接影響生產。對我而言,DRY對生產代碼有意義,但WET(Write Everything Twice)也適用於單元測試代碼。

要直接回答你的問題,你可以使用循環,你可以結合測試,但我懷疑他們沒有多大幫助。

例如

@Test 
public void firstNameMaxPermittedLength() { 
    Userdata userdata = minimumRequirements(); 
    userdata.setFirstName(utf8Chars(64)); 
    assertNoViolations(cut.validate(userdata)); 

    userdata.setFirstName(utf8Chars(65)); 
    assertSingleViolation(cut.validate(userdata)); 
} 
+0

結合測試會讓國際海事組織做更多的傷害,因爲在失敗,測試用例不會被執行。一般來說,我同意WET的測試代碼 - 但在這種情況下,我的測試課程已經完成一半,已經接近600 LoC。另外,我擔心忘記一些檢查,並且必須有選擇地實現〜10個字段,這很容易出現人爲錯誤。 –

+1

目標應該是沒有失敗的測試。無論是一次測試失敗而是兩次測試失敗,它確實沒有太大的區別。我可以看到使用反射或數據驅動測試生成測試的唯一方式,但這往往會結合更多的測試用例。 –

+1

我想你是對的。所以我會重構每個字段的一個方法,調用類似'assertProperStringLengthValidation(cut,「firstName」,64);' –

0

如果所有這些領域做同樣的事情和名稱一致,爲什麼不問問類通過所有以set開始的那些它的方法列表,循環,並盡一切是你需要做?

+0

不幸的是,他們不完全一樣 - 有幾個國家的驗證是相等的,並且有幾個長度驗證沒有相同的長度限制 –

+1

好。如果所需行爲不同,那麼測試應該是不同的,這似乎是不可避免的。對於它的價值,在類似的情況下,我創建了一組定義字段有效性的註釋,然後讓驗證器查看這些字段而不是每個字段的硬編碼。然後,您只需要針對各種註釋測試驗證器。從那以後,Java內置了類似的東西:http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html,儘管我還沒有用過它。這是否會滿足您的需求,並根本不需要測試您自己的驗證? –

+0

非常感謝您的建議。我以前使用過驗證API,但是我沒有在我的問題中添加一些字段,這些字段依賴於其他字段,這對於使用驗證API來實現並不是微不足道的。 –