簡體   English   中英

在測試復雜行為時,可以在一個單元測試中包含多個斷言嗎?

[英]Is it OK to have multiple assertions in a unit test when testing complex behavior?

這是我的具體情況。

我有一個QueryQueue類, QueryQueueQueryTask類包裝在ArcGIS API for Flex中。 這使我可以輕松地將多個查詢任務排隊等待執行。 調用QueryQueue.execute()遍歷隊列中的所有任務,並調用它們的execute方法。

接收並處理所有結果后, QueryQueue將調度完成的事件。 我班的接口很簡單。

public interface IQueryQueue
{
    function get inProgress():Boolean;
    function get count():int;

    function get completed():ISignal;
    function get canceled():ISignal;

    function add(query:Query, url:String, token:Object = null):void; 
    function cancel():void;
    function execute():void;
}

為了使QueryQueue.execute方法成功,必須發生一些事情。

  1. 必須對每個查詢任務一次調用task.execute
  2. 結果等待處理時, inProgress = true
  3. 處理結果后, inProgress = false
  4. completed結果后調度completed
  5. canceled從未被調用
  6. 隊列中完成的處理正確處理並打包了查詢結果

我正在努力將這些測試分解為可讀,邏輯和可維護的測試。

從邏輯上講,我正在測試一種狀態 ,即成功執行狀態。 這表明一個單元測試可以斷定上面的#1至#6是正確的。

[Test] public mustReturnQueryQueueEventArgsWithResultsAndNoErrorsWhenAllQueriesAreSuccessful:void

但是,測試的名稱並不能說明所有問題,因為它沒有描述要通過測試就必須真實的所有事物。

在線閱讀(包括在此處以及在developers.stackexchange.com上 ),有一個相當大的陣營,它斷言單元測試應該只有一個斷言(作為准則)。 結果,當測試失敗時,您會確切地知道失敗的原因(即,inProgress未被設置為true,完成顯示多次,等等。)您可能會得到更多(但從理論上講更簡單更清晰)的測試,如下所示:

[Test] public mustInvokeExecuteForEachQueryTaskWhenQueueIsNotEmpty():void
[Test] public mustBeInProgressWhenResultsArePending():void
[Test] public mustNotInProgressWhenResultsAreProcessedAndSent:void
[Test] public mustDispatchTheCompletedEventWhenAllResultsProcessed():void
[Test] public mustNeverDispatchTheCanceledEventWhenNotCanceled():void
[Test] public mustReturnQueryQueueEventArgsWithResultsAndNoErrorsWhenAllQueriesAreSuccessful:void
// ... and so on

在測試中可能會產生大量重復的代碼,但是可以通過適當的setupteardown方法將其最小化。

盡管此問題與其他問題類似,但我正在尋找針對此特定方案的答案,因為我認為它很好地表示了一個復雜的單元測試方案,該方案具有多個狀態和行為,需要進行驗證。 不幸的是,許多其他問題都沒有示例,或者這些示例沒有展示復雜的狀態和行為。

我認為可能會有很多,這里有幾件事:

  1. 如果您必須為一種方法測試很多東西,那么這可能意味着您的代碼可能在一種方法中做得太多( 單一職責原則)
  2. 如果您不同意以上內容,那么我接下來要說的是,您所描述的更多是集成/驗收測試。 它允許多個斷言,並且那里沒有問題。 但是請記住,如果您要進行自動測試(安全測試與不安全測試),則可能需要將其放到單獨的測試部分中。
  3. 和/或,是的,首選方法是分別測試每個部分,因為這就是單元測試。 我可以建議的最接近的東西,這是關於您對代碼的容忍度,以使其具有完美的測試...是針對一個對象檢查一個對象(因此,您需要做一個斷言,實質上就是對所有對象進行一次測試)。 但是,反對這一觀點的論點是,是的,它通過了每個測試的一個斷言,但是您仍然失去了表現力。

最終,您的目標應該是通過專注於SOLID原則來追求理想的狀態(每個單元測試一個斷言),但是最終您確實需要完成工作,否則編寫軟件沒有任何實際意義(至少我認為: ))。

讓我們專注於您首先確定的測試。 除了最后一個( mustReturnQueryQueueEventArgs... )之外,其他所有都是好東西,我可以立即告訴那里正在測試什么(這是一個很好的信號,表明它們具有描述性,並且很可能很簡單)。

唯一的問題是您的最后一次測試。 請注意,測試名稱中單詞“ and”“ with”“ or”的廣泛使用通常會引起問題。 目前尚不清楚應該做什么。 返回正確的結果首先想到的是,但有人可能會說這是一個模糊的術語? 這是正確的,這是模糊的。 但是,您經常會發現這確實是相當普遍的要求,並按方法/操作合同進行了詳細說明。

在您的特定情況下,我將簡化上一次測試,以驗證是否返回了正確的結果,僅此而已。 您已經測試了導致結果建立的狀態,事件和事物,因此無需再次進行測試。

現在,您提供的鏈接中的建議實際上是非常好的建議, 通常 ,我建議您堅持使用它們(對一個測試使用單個斷言)。 問題是, 單個斷言真正代表什么? 測試結束時有1行代碼? 然后讓我們考慮這個簡單的示例:

// a method which updates two fields of our custom entity, MyEntity
public void Update(MyEntity entity)
{
    entity.Name = "some name";
    entity.Value = "some value";
}

該方法合同將執行這兩個操作。 通過成功,我們了解要正確更新的實體。 如果其中一個由於某種原因失敗,則將方法作為一個單元視為失敗。 您可以看到前進的方向; 您將擁有兩個斷言,或者僅出於測試目的編寫自定義比較器。

不要被單一的主張所欺騙; 這與代碼行數或斷言數量無關(但是,在大多數測試中,您編寫的確會映射為1:1),而是斷言單個單元 (在上面的示例中, update被視為一個單元)。 實際上,單位可能是多種事物,彼此之間毫無任何意義。

這正是您鏈接引號的問題之一(Roy Osherove):

我的指導原則通常是每次測試都測試一個邏輯概念。 您可以在同一對象上具有多個斷言。 它們通常是要測試的相同概念。

都是關於概念/責任; 不是斷言的數量。

我不熟悉flex,但是我想我在單元測試方面有很好的經驗,因此您必須知道單元測試是一種哲學,因此對於第一個答案,可以進行多重聲明,但是如果您測試相同的行為,單元測試中的重點始終是非常易於維護和簡單的代碼,否則單元測試將需要單元測試來進行測試! 因此,我的建議是,如果您是單元測試的新手,請不要使用多個斷言,但是如果您在單元測試方面有豐富的經驗,那么您會知道何時需要使用它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM