2011-01-19 67 views
4

讓我們假裝我有3個類,每個類都有一個負責任的(和方法)。我們還假裝這些類有接口來促進依賴注入。類A調用類B的接口,類B調用類C的接口。如果類C拋出NotAPrimeNumberException(如果說,int參數不是質數),我希望有一個單元測試可以確保如果傳入的參數不是素數,C會拋出一個NotAPrimeNumberException異常。到現在爲止還挺好。單元測試多個例外 - 何處停止?

我目前認爲,單元測試提供了所有需要理解被測方法行爲的文檔。所以前面提到的單元測試就像MakeSureNotAPrimeNumberExceptionIsThrownIfNumberisNotPrimeTest()。

B類知道類C可以拋出一個NotAPrimeNumberException。如果我想讓NotAPrimeNumberException冒出B類,我應該爲B類編寫一個單元測試,以某種方式檢查在某些情況下是否引發NotAPrimeNumberException? A類呢?如果A也讓NotAPrimeNumberException冒出來,它是否也有一個單元測試呢?

我擔心的是,如果我不在B類中編寫單元測試,那麼類B的消費者不會意識到類B可以拋出這種類型的異常。但是,如果我要寫單元測試,那麼我必須強迫對類B的調用拋出一個NotAPrimeNumberException,而不是處理類B中的異常,這有點愚蠢。如果我不寫一個單元測試,是否有適當的方式(如果有)在API中記錄此異常可能發生?

獎勵的問題是你如何在NUnit中使用Rhino-mocks來實現這個功能?這當然取決於前兩個問題。

+1

當預期的行爲發生時,測試應該通過。即使這種行爲正在拋出異常。 – 2011-01-19 17:19:38

回答

2

如果A類和B類期望來自與之協作的類的某些行爲,那麼您應該測試以確保這些行爲發生。如果你想要避免你通過從A到B到C的硬連線呼叫來進入的巨大泥球,那麼你應該應用好萊塢原理/ DIP以打破這種依賴關係。 A應該依賴於IB(B的接口)而不是B的實例。然後,A的測試可以簡單地斷言A做它應該做的事情,包括在面對IB拋出的異常(或者忽略它們) 。同樣,B可以依賴於C,IC的接口,而B的測試可以類似地忽略C可能做或不可做的細節,而只關注B所做的事情。

將A連接到B連接到C的集成測試將能夠驗證這些類是否正確協作,並且還可以確保在C中產生的異常從A中冒出來,如果這是你想要發生。如果後來發現C只是返回null而不是引發異常,那麼你的集成測試就會失敗,你會發現新的行爲已經發生。但是,我不會傾向於用測試表明如果事情破壞異常會冒泡,因爲這是默認行爲,所以我的個人單元測試會混亂不堪。相反,我可能會測試我提出的任何新異常在預期時被拋出,並且我執行的任何異常處理都按我的預期工作。這是因爲在單元測試中,您應該只測試受測試的代碼 - 而不是合作者的行爲。

+0

對不起,我只是更新了評論,以便更清楚一點。我只是假設有嘲弄的接口和依賴注入,但在問題中沒有這麼說。我的問題的真正意義在於爲方法編寫單元測試,因爲依賴引發異常,並且這是否有意義,以幫助確保我的單元測試記錄我的方法的所有行爲。 – jakejgordon 2011-01-19 18:58:03

+0

你用你的評論回答了你的問題。你的單元測試應該記錄「我的方法的所有行爲」,而不是*你的方法可能調用的方法的行爲。只要你這樣做,你就很好。如果你的方法處理異常的方法很重要,那麼很容易(使用mock或者fakes)來設置一個測試,在這個測試中拋出一個異常,並驗證它是否正確地執行了它。如果這是重要的行爲,這對我來說似乎是有效的單元測試。但是嘲笑交互,不要依賴特定的其他方法實現。 – ssmith 2011-01-19 19:38:13

1

您應該對B和C使用模擬並調用其方法。這是UNIT測試的要點 - 您只需測試一個代碼單元,而不是依賴關係。如果你不想在這種情況下使用模擬,我認爲如果在其中一個依賴項中引發異常,應該沒有特殊的條件允許它通過。它應該失敗。第一個原因:你正在測試的確切代碼實際上並沒有工作(你正在測試它作爲一個整體,依賴)。第二個原因:你的測試如此混亂。

請記住保持簡單。

+0

對不起,我剛剛更新了評論,以便更清楚一點。我只是假設有嘲弄的接口和依賴注入,但在問題中沒有這麼說。我的問題的真正意義在於爲方法編寫單元測試,因爲異常拋出了依賴性,以及這是否有意義,以幫助確保我的單元測試記錄我的方法的所有行爲。 – jakejgordon 2011-01-19 18:50:32

0

如果類A和類B不需要關心是否存在異常,那麼您不應該單元測試該功能。如果他們對該異常有一些特定的行爲(也許他們用不同的異常包裝它或者用嗶嗶聲來抱怨並大聲抱怨),並且您需要驗證該行爲,那麼您應該這樣做。

在前一種情況下,我會做出不進行單元測試A和B的決定,以避免讓我的測試變得脆弱。如果在面對某個異常時驗證A和B的行爲並不重要,那麼您不希望將這些測試與不同模塊的行爲聯繫起來。

2

這裏有一些想法:

  1. C類設計拋出NotAPrimeNumberException。因此,您應該有一個測試,驗證是否在預期時拋出異常。
  2. 在這種情況下是否測試類B或A取決於它們是否處理異常。如果類C需要在類C拋出異常時執行某些操作,那麼您應該測試該行爲。如果B類只是讓這個異常「通過」,那麼你不應該爲它進行測試。否則,您還需要測試NullReferenceException,ArithmeticOverflowException,HttpException和所有其他異常類型的行爲。這是單元測試ad nausaeam,並沒有幫助。
  3. 關於C類行爲變化的問題,您需要考慮幾件事情。任何依賴於改變的行爲的測試也應該改變。 (既然你在做TDD,你在改變行爲之前改變了測試,對嗎?)另外,模擬C類異常拋出行爲的任何模擬也需要改變。如果它從不拋出異常,那麼在類C拋出異常時測試會發生什麼情況沒有用處。最後的考慮是有些問題,我不知道如何以正式的方式進行管理。我對進一步的想法感興趣。