2011-02-17 34 views
9

我有一個DTO,我從請求對象填充,並且請求對象有很多字段。我想寫一個測試來檢查populateDTO()方法是否將值放在正確的位置。如果我遵循每個測試的一個斷言規則,我將不得不編寫大量測試來測試每個字段。另一種方法是在單個測試中寫入多個斷言。是否真的建議遵循每個測試規則的一個斷言,或者我們能否放鬆這些情況。我如何處理這個問題?如何避免JUnit測試中的多重聲明?

回答

0

該規則是否擴展爲循環?考慮這個

Collection expectedValues = // populate expected values 
populateDTO(); 
for(DTO dto : myDtoContainer) 
    assert_equal(dto, expectedValues.get(someIndexRelatedToDto)) 

現在我不是那麼大的確切的語法,但這只是我正在看的概念。

編輯: 的意見後...

答案是... 都能跟得上!

原理存在的原因是,您可以識別對象的哪些部分失敗。如果你用一種方法擁有它們,你將只碰到一個斷言,然後是下一個,然後是下一個,你不會看到它們全部。

所以,你可以有兩種方式這一個:

  1. 一種方法,較少的樣板代碼。
  2. 許多方法,在試運行

這是給你更好的報告,都有起伏。 3.列表項目

+0

DTO是POJO ..並且請求也是POJO。所以assertes很可能看起來像assertEqual(「abc」,dto.getName());等。 – Ravi 2011-02-17 05:43:52

+0

所以你想避免只是`assert(dto.equals(mockObject)`,那是你在說什麼?你想斷言每個單獨的元素,而不違反每個原則? – corsiKa 2011-02-17 05:46:18

+0

是的,我想斷言每個單獨的元素,而不違反每個原則。只是你知道,請求是一個JAXB生成的對象。 – Ravi 2011-02-17 06:00:06

1

您可以有一個parameterized test其中第一個參數是屬性名,第二個參數是期望值。

-1

或者你可以做一些解決方法。

import junit.framework.Assert; 
import org.junit.After; 
import org.junit.AfterClass; 
import org.junit.Before; 
import org.junit.BeforeClass; 
import org.junit.Test; 

public class NewEmptyJUnitTest { 

    public NewEmptyJUnitTest() { 
    } 

    @BeforeClass 
    public static void setUpClass() throws Exception { 
    } 

    @AfterClass 
    public static void tearDownClass() throws Exception { 
    } 

    @Before 
    public void setUp() { 
    } 

    @After 
    public void tearDown() { 
    } 


    @Test 
    public void checkMultipleValues() { 
     String errMessages = new String(); 

     try{ 
      this.checkProperty1("someActualResult", "someExpectedResult"); 
     } catch (Exception e){ 
      errMessages += e.getMessage(); 
     } 

     try{ 
      this.checkProperty2("someActualResult", "someExpectedResult"); 
     } catch (Exception e){ 
      errMessages += e.getMessage(); 
     } 

     try{ 
      this.checkProperty3("someActualResult", "someExpectedResult"); 
     } catch (Exception e){ 
      errMessages += e.getMessage(); 
     } 

     Assert.assertTrue(errMessages, errMessages.isEmpty()); 


    } 



    private boolean checkProperty1(String propertyValue, String expectedvalue) throws Exception{ 
     if(propertyValue == expectedvalue){ 
      return true; 
     }else { 
      throw new Exception("Property1 has value: " + propertyValue + ", expected: " + expectedvalue); 
     } 
    } 

     private boolean checkProperty2(String propertyValue, String expectedvalue) throws Exception{ 
     if(propertyValue == expectedvalue){ 
      return true; 
     }else { 
      throw new Exception("Property2 has value: " + propertyValue + ", expected: " + expectedvalue); 
     } 
    } 

     private boolean checkProperty3(String propertyValue, String expectedvalue) throws Exception{ 
     if(propertyValue == expectedvalue){ 
      return true; 
     }else { 
      throw new Exception("Property3 has value: " + propertyValue + ", expected: " + expectedvalue); 
     } 
    } 
} 

也許不是最好的辦法,如果過度使用,可以混淆......但這是一種可能性。

+0

我會說任何使用過度。所有這些樣板只是爲了解決「每次測試中的單個斷言」? – merryprankster 2012-09-28 12:20:31

7

保持它們分開。單元測試應該告訴你哪個單元失敗。使它們保持分離還可以讓您快速隔離問題而不需要經過漫長的調試周期。

0

[告誡:我用Java/JUnit的很「unfluent」,所以在下面的詳細信息的錯誤提防]

有幾個方法可以做到這一點:

1)寫入多個斷言在相同的測試中。如果你只測試一次DTO代,這應該沒問題。你可以從這裏開始,並在開始傷害時轉向另一個解決方案。

2)寫一個幫手斷言,例如, assertDtoFieldsEqual,傳入預期的和實際的DTO。在助手聲明中,你分別聲明每個字段。這至少給你每個測試只有一個斷言的錯覺,並且如果你測試多個場景的DTO生成,它會讓事情變得更加清晰。

3)爲檢查每個屬性並實現toString的對象實現equals,以便至少可以手動檢查斷言結果以找出哪個部分不正確。

4)對於生成DTO的每個場景,創建一個單獨的測試夾具,用於生成DTO並在setUp方法中初始化期望的屬性。爲測試每個屬性創建一個單獨的測試。這也會導致很多測試,但它們至少只會是單線測試。例如,在僞代碼:

public class WithDtoGeneratedFromXxx : TestFixture 
{ 
    DTO dto = null; 

    public void setUp() 
    { 
    dto = GenerateDtoFromXxx(); 
    expectedProp1 = ""; 
    ... 
    } 

    void testProp1IsGeneratedCorrectly() 
    { 
    assertEqual(expectedProp1, dto.prop1); 
    } 
    ... 
} 

如果你需要測試在不同情況下的DTO的生成和選擇這最後的方法,它馬上會變成乏味寫所有這些測試。如果是這種情況,你可以實現一個抽象的基礎設備,省去了關於如何創建DTO和設置派生類的期望屬性的細節。僞代碼:

abstract class AbstractDtoTest : TestFixture 
{ 
    DTO dto; 
    SomeType expectedProp1; 

    abstract DTO createDto(); 
    abstract SomeType getExpectedProp1(); 

    void setUp() 
    { 
    dto = createDto(); 
    ... 
    } 

    void testProp1IsGeneratedCorrectly() 
    { 
    assertEqual(getExpectedProp1(), dto.prop1); 
    } 
    ... 
} 


class WithDtoGeneratedFromXxx : AbstractDtoTest 
{ 
    DTO createDto() { return GenerateDtoFromXxx(); } 
    abstract SomeType getExpectedProp1() { return new SomeType(); } 
    ... 
} 
4

是否真的有推薦only one assert per unit test?是的,有人提出這個建議。他們是對的嗎?我不這麼認爲。我很難相信這樣的人很長一段時間都在真正的代碼上工作過。

所以,imangine你有一個你想單元測試的增變器方法。增變器具有某種你想要檢查的效果或效果。通常,增變器的預期效果數量很少,因爲許多效果表明增變器的設計過於複雜。每一個效果都有一個斷言,每個斷言一個測試用例,你不需要每個mutator的很多測試用例,所以這個建議看起來並不是那麼糟糕。

但是這個推理的缺陷在於那些測試只關注增變器的預期效果。但是如果增變器有一個bug,它可能會有意想不到的副作用。測試做出愚蠢的假設,即代碼沒有一整套錯誤,並且未來的重構不會引入此類錯誤。當最初編寫該方法時,作者可能會明白特定的副作用是不可能的,但重構和添加新功能可能會導致此類副作用。

測試長壽代碼的唯一安全方法是檢查增變器是否沒有意想不到的副作用。但你如何測試這些?大多數類有一些不變量:沒有增變器可以改變的事情。例如,容器的size方法永遠不會返回負值。實際上,每個不變量都是每個增變器(以及構造器)的後置條件。每個增變器通常還具有一組不變量,用於描述如何改變其死亡而不是。例如,一個sort方法不會改變容器的長度。實際上,類和增變不變量是過去的增變器調用的後置條件。爲他們添加斷言是檢查意外副作用的唯一方法。

那麼,只需添加更多測試用例?在實踐中,不變量的數量乘以要測試的增變器的數量很大,因此每個測試的一個斷言會導致很多測試用例。關於你的不變式的信息分散在許多測試用例中。設計更改以調整一個不變量將需要更改許多測試用例。它變得不切實際。它更適合爲mutator設置參數化測試用例,它使用幾個斷言來檢查mutator的幾個不變量。