2009-11-23 351 views
4

說我們有這個超簡單的類hierchy:單元測試

public class SomeMath 
{ 
    public int Add(int x, int y) 
    { 
     return x + y; 
    } 
} 

public class MoreMath : SomeMath 
{ 
    public int Subtract(int x, int y) 
    { 
     return x - y; 
    } 
} 

我應該寫爲Add方法測試時,我寫的MoreMath類測試?或者我在測試SomeMath課程時是否只關心該方法?更一般地說:我應該測試一個類的所有方法,還是應該只測試「新」方法?

我可以想出雙方的一些原因。例如,當測試所有的方法時,你最終會不止一次地測試同一個東西,這不太好,可能會變得乏味。但是如果您不測試所有方法,SomeMath中的更改可能會打破MoreMath的使用情況?這也會是一件壞事。我想這可能也取決於案件。就像它擴展了一個我可以控制或不控制的類。但無論如何,我是一個全面測試新手,所以我很想知道人們比我想的更聰明:-)

+1

考慮到你問了一個沒有明確正確或錯誤答案的問題,我認爲在7分鐘後接受答案是一個壞主意。 – 2009-11-23 13:19:04

+0

好點。注意:) – Svish 2009-11-23 13:26:26

回答

0

首先,我認爲有可能影響你的決定做一個或另一個至少有兩個因素:

  • 什麼是繼承的目的是什麼?
  • 有問題的測試目的是什麼?

在TDD方案中,我傾向於爲MoreMath編寫單個測試用例,驗證它是否從SomeMath派生,然後考慮從SomeMath繼承的所有成員都將被覆蓋。

但是,這意味着從設計的角度來看,MoreMath從SomeMath派生出來是一個重要的設計方面。如果以多態的方式使用SomeMath,情況肯定會如此。但是,如果您只是使用繼承來重用(不建議,但是),情況並非如此。

在後一種情況下(繼承用於重用),父類和子類之間沒有概念上的聯繫,您可能會試圖在將來打破繼承。在這種情況下,通過一項驗證父母是否正確的測試是一個不好的保障。

從質量保證(QA)的角度來看,每個班級的每個成員都應該嚴格測試。這意味着即使測試代碼相同,您也應該重複每個子類的測試代碼,因爲您需要驗證是否以意外的方式覆蓋了虛擬方法。但是,要保持乾燥,您可以將它們寫入參數化測試,或者使用諸如Pex之類的工具。

就我個人而言,我很少進入QA階段。通常,在TDD期間創建的測試套件是一個合適的安全網......但這一切都取決於您所構建的軟件類型。

+0

作出這個答案,因爲它是涵蓋更多的角度:) – Svish 2009-11-23 14:52:09

5

通常我不會測試子類中的行爲,除非子類改變行爲從父類預期的。如果它使用相同的行爲,則不需要對其進行測試。如果您打算對父類進行重大更改,但子類仍然需要舊的行爲,那麼我會首先在子類中創建測試以定義其所需的行爲,然後在父測試中進行更改並隨後進行更改代碼更改。這遵循YAGNI原則 - 你不會需要它 - 並推遲實施兒童測試,直到他們真正有目的。

+1

聽起來像一個很好的做法:) – Svish 2009-11-23 13:11:09

+0

我認爲這個答案假設單元測試只寫在TDD時尚。在我看來,這只是答案的一半,但請參閱我的答案進行更全面的檢查。 – 2009-11-23 13:16:54

1

我只測試SomeMath類的Add方法。如果MoreMath只繼承它,並且絕對沒有新東西,那麼爲兩者編寫測試將是純粹的重複代碼,僅此而已。對於這些事情來說,做一點務實的事情總是件好事。

0

我會讓我的測試類MoreMath繼承SomeMath的測試類,從而繼承該類的所有測試。這意味着我只需爲新功能編寫額外的測試,但所有子類的功能都經過了充分測試。

1

在我目前的地方,我們遇到了類似的問題,我們希望開發接口,但確保接口的每個實現都正確運行(例如,不管該層的實現如何,不同的數據層應該表現相同)。在路上,我們解決了它(與NUnit的)是有單元測試中的繼承結構:

public interface ICalculator 
{ 
    public int Add(int a, int b) 
} 

public class Calculator : ICalculator 
{ 
    public int Add(int a, int b) { return a + b; } 
} 

public class TestCalculatorInterface 
{ 
    public abstract SomeMath GetObjectToTest(); 

    [Test] 
    public void TestAdd() 
    { 
     var someMath = GetObjectToTest(); 
     ... 
    } 
}  

[TestFixture] 
public class TestCalculator: TestCalculatorInterface 
{ 
    public virtual Calculator GetObjectToTest() { return new Calculator(); } 
} 

我們不會對基本接口測試[的TestFixture]屬性,但有[測試]屬性的所有測試方法。 TestCalculator類是[TestFixture],但它繼承了基類中的所有測試,它使子類僅負責提供對象以測試該接口。

我會爲你的情況採用類似的模式,所以測試針對所有類運行,但只寫一次。

+0

我實際上做了一些類似的事情我自己一次:) – Svish 2009-11-23 14:51:28