2010-07-21 90 views
1

我有一個應用程序服務(即大型方法)負責協調多個業務對象之間的交互。從本質上說,它需要一個包含客戶信息和發票的系統的DTO,並根據各種業務規則將其翻譯並導入到不同的系統中。正確處理單元測試「複雜」應用程序服務

public void ProcessQueuedData() 
    { 
     var queuedItems = _importServiceDAL.LoadQueuedData(); 

     foreach (var queuedItem in queuedItems) 
     { 
      var itemType = GetQueuedImportItemType(queuedItem); 

      if (itemType == QueuedImportItemType.Invoice) 
      { 
       var account = _accountDAL.GetAccountByAssocNo(queuedItem.Assoc); 
       int agentAccountID; 

       if (!account.IsValid) 
       { 
        agentAccountId = _accountDAL.AddAccount(queuedItem.Assoc); 
       } 
       else 
       { 
        agentAccountId = account.AgentAccountID; 
       } 

       /// Do additional processing TBD 
      } 
     } 
    } 

對於單元測試,它是正確的假設的服務內的整個過程中應在粒狀步步基礎進行測試,類似於以下?

ImportService_ProcessQueuedData_CallsDataAccessLayer_ToLoadQueue

ImportService_ProcessQueuedData_WithQueuedItemToProccess_ChecksIfAccountExists

ImportService_ProcessQueuedData_WithInvoice_CallsDALToCreateAccountIfOneDoesNotExist

下面是一個典型的測試:

[TestMethod()] 
    public void ImportService_ProcessQueuedData_WithInvoice_CallsDALToCheckIfAgentAccountExists() 
    { 
     var accountDAL = MockRepository.GenerateStub<IAccountDAL>(); 
     var importServiceDAL = MockRepository.GenerateStub<IImportServiceDAL>(); 

     importServiceDAL.Stub(x => x.LoadQueuedData()) 
      .Return(GetQueuedImportItemsWithInvoice()); 

     accountDAL.Stub(x => x.GetAccountByAssocNo("FFFFF")) 
      .IgnoreArguments() 
      .Return(new Account() { AgentAccountId = 0 }); 

     var importSvc = new ImportService(accountDAL, importServiceDAL); 

     importSvc.ProcessQueuedData(); 

     accountDAL.AssertWasCalled(a => a.GetAccountByAssocNo("FFFFF"), o => o.IgnoreArguments()); 
     accountDAL.VerifyAllExpectations(); 
    } 

我的問題是我最終在這些測試中做了這麼多設置,它變得很脆弱。這是否是正確的方法,如果是的話,在每個粒度測試中避免重複所有這些設置的指針是什麼?

回答

0

我對您的特定應用程序瞭解不多,因此我無法真正提出任何具體建議。也就是說,這種面向過程的測試聽起來像是可以成爲採用技術的好選擇。對於Java,我熟悉ModelJUnit工具(公平披露:我曾被聘爲該工具的開發人員),但似乎有一個與C#相關的工具NModel,以及一本附帶的書籍Model-based Software Testing and Analysis in C#。我這樣說的原因是,也許你可以在整個過程中隨意地散步,把你的設置代碼放在一個地方,讓抽象測試代爲你完成大部分的辛苦工作。

+0

謝謝吉安...你建議NModel B/C可能的輸入條件的感知數量是巨大的?如果輸入類型的組合更受限制,您是否仍然會推薦這種方法?我試圖測試的整個過程實際上只有6個條件分支和已知數量的條件在這個if-then邏輯中發揮作用。再次感謝您的洞察力。 – nerdn 2010-07-22 12:44:40

+0

它更像是一個連續的過程,在每一步都有可能出現錯誤情況,並且可能有通過這些行爲的多種不同途徑。這就是基於模型的測試真正閃耀的地方 - 您只需對模型中的所有狀態進行編碼,並讓測試系統找出可能的路徑。 – Gian 2010-07-22 13:02:11

0

就我個人而言,我會嘗試測試所有的代碼片段,但不一定每個部分作爲自己的測試。一項測試會檢查具有有效帳戶的發票是否已通過。第二個測試檢查具有無效帳戶的發票是否創建新帳戶。當然,我會嘲笑dals,因此沒有數據被添加到數據庫中。這也允許嘲笑不應該採取任何操作的例外和情況(隊列中沒有東西,也許沒有發票)。

0

我同意佩德羅。您的第一個示例測試(ImportService_ProcessQueuedData_CallsDataAccessLayer_ToLoadQueue)不是必需的,因爲其他測試將隱式地測試LoadQueuedData是否被調用 - 如果不是,它們將沒有數據可操作。您希望對每條路徑都進行測試,但不需要爲方法中的每行代碼單獨進行測試。如果通過條件分支有六條路徑,則需要六個測試。

如果我想變得很花哨,我可能會考慮利用對象多態來減少if語句的數量並簡化測試。例如,對於每個QueuedImportItemType,您可以擁有一個不同的「處理程序」,並將處理該類型項目的邏輯放在該處,而不是您的大型Process方法。分解迭代的邏輯以及如何處理每種類型的項目使得它們更易於單獨測試。