c# 單元測試c# - 你如何嘲笑C#中的文件系統進行單元測試?




2017單元測試 單元測試c#範例 (12)

我們目前使用專有的數據引擎,其API不作為接口公開,所以我們很難單元測試我們的數據訪問代碼。 然後,我也與馬特和約瑟夫的方法一起去了。

是否有任何庫或方法在C#中編寫文件系統來編寫單元測試? 在我目前的情況下,我有方法檢查某個文件是否存在並讀取創建日期。 未來我可能需要更多。


您可能將不得不建立一個合約,以便從文件系統中定義您需要的東西,然後為這些功能編寫封裝。 此時你可以模擬或者刪除實現。

例:

interface IFileWrapper { bool Exists(String filePath); }

class FileWrapper: IFileWrapper
{
    bool Exists(String filePath) { return File.Exists(filePath); }        
}

class FileWrapperStub: IFileWrapper
{
    bool Exists(String filePath) 
    { return (filePath == @"C:\myfilerocks.txt"); }
}

我不確定你會如何模擬文件系統。 你可以做的是寫一個測試夾具設置,創建一個文件夾等與測試的必要結構。 拆卸方法會在測試運行後將其清理乾淨。

編輯補充說:再想一想,我不認為你想嘲笑文件系統來測試這種類型的方法。 如果您將文件系統模擬為在某個文件存在的情況下返回true,並在您檢測該文件是否存在的方法中使用該文件系統,那麼您就不會進行任何測試。 如果你想測試一個依賴於文件系統的方法,但是文件系統活動並不是被測方法不可或缺的一部分,那麼在嘲笑文件系統的時候就會有用。


這個虛構的庫現在存在,有一個用於System.IO.Abstractions的NuGet包,它抽像出System.IO命名空間。

還有一套測試助手System.IO.Abstractions.TestingHelpers,它在撰寫本文時僅部分實施,但是它是一個非常好的起點。


在測試中嘲笑文件系統是很困難的,因為.NET文件API並不是真正基於可能被嘲笑的接口或可擴展類。

但是,如果你有自己的功能層來訪問文件系統,你可以在單元測試中嘲笑它。

作為嘲笑的替代方法,可以考慮將您需要的文件夾和文件作為測試設置的一部分,並在拆卸方法中刪除它們。


您可以使用Microsoft Fakes來做到這一點,而無需更改您的代碼庫,因為它已經被凍結。

首先為System.dll 生成偽裝 - 或者其他任何包,然後模擬預期的回報,如下所示:

using Microsoft.QualityTools.Testing.Fakes;
...
using (ShimsContext.Create())
{
     System.IO.Fakes.ShimFile.ExistsString = (p) => true;
     System.IO.Fakes.ShimFile.ReadAllTextString = (p) => "your file content";

      //Your methods to test
}


我遇到以下解決方案:

  • 寫集成測試,而不是單元測試。 為了這個工作,你需要一個簡單的方法來創建一個文件夾,在那裡你可以轉儲的東西,而不用擔心其他測試干擾。 我有一個簡單的TestFolder類,它可以創建一個獨特的每個測試方法文件夾來使用。
  • 寫一個可嘲弄的System.IO.File。 這是創建一個IFile.cs 。 我發現使用它通常會以測試證明您可以編寫模擬語句,但在IO使用率很小時使用它。
  • 檢查你的抽象層,並從類中提取文件IO。 為此創建一個接口。 其餘的使用集成測試(但這將是非常小的)。 這與上面的不同之處在於,不是在執行file.Read你寫意圖,說ioThingie.loadSettings()
  • systemioabstractions.codeplex.com 。 我還沒有使用過,但這是我最喜歡玩的一個。

根據我寫的內容,我最終使用了上面所有的方法。 但是大多數情況下,當我編寫單元測試碰到IO時,我最終認為抽像是錯誤的。


編輯:安裝NuGet包System.IO.Abstractions

當這個答案最初被接受時,這個包不存在。 原始答案是針對以下歷史情況提供的:

你可以通過創建一個接口來實現:

interface IFileSystem {
    bool FileExists(string fileName);
    DateTime GetCreationDate(string fileName);
}

並創建一個使用System.IO.File.Exists()等的“真實”實現。然後,您可以使用模擬框架來模擬此接口; 我推薦Moq

編輯:有人完成了這一點,並在此發佈在線。

我使用這種方法在IClock接口中模擬出DateTime.UtcNow(對我們的測試來說,能夠控制時間流程非常有用!),而且更傳統上是一個ISqlDataAccess接口。

另一種方法可能是使用TypeMock ,它允許你攔截對類的調用並將其存儲。 然而,這確實花了錢,並且需要安裝在整個團隊的PC和構建服務器上才能運行,而且它顯然不適用於System.IO.File,因為它不能存儲mscorlib

您也可以接受某些方法不是單元測試,並在單獨的慢速運行集成/系統測試套件中進行測試。


回答你的具體問題:不,沒有庫允許你模擬文件I / O調用(我知道的)。 這意味著“正確地”單元測試你的類型將要求你在定義類型時考慮到這個限制。

關於我如何定義“適當”單元測試的快速說明。 我相信單元測試應該確認你得到了預期的輸出(是一個例外,調用一個方法等)提供了已知的輸入。 這允許您將單元測試條件設置為一組輸入和/或輸入狀態。 我發現這樣做的最好方式是使用基於接口的服務和依賴注入,以便通過通過構造函數或屬性傳遞的接口提供類型外部的每個責任。

所以,考慮到這一點,回到你的問題。 我通過創建一個IFileSystemService接口和一個FileSystemService實現來嘲笑文件系統調用,該實現只是mscorlib文件系統方法的一個外觀。 我的代碼然後使用IFileSystemService而不是mscorlib類型。 這允許我在應用程序運行時插入標準FileSystemService ,或者在單元測試中模擬IFileSystemService 。 無論應用程序如何運行,應用程序代碼都是相同的,但底層基礎結構允許輕鬆測試代碼。

我會承認,使用mscorlib文件系統對象的包裝是一件很痛苦的事情,但在這些特定的場景中,測試變得如此簡單和可靠,值得額外的工作。


創建一個界面並嘲笑它進行測試是最乾淨的方法。 然而,作為替代品,你可以看看微軟的Moles框架。


私有方法只能在同一個類中訪問。 因此,無法從任何測試類測試目標類的“私有”方法。 一種解決方法是您可以手動執行單元測試,也可以將方法從“私有”更改為“受保護”。

然後,只能在定義類的同一個包中訪問受保護的方法。 因此,測試目標類的受保護方法意味著我們需要在與目標類​​相同的包中定義測試類。

如果以上所有內容都不符合您的要求,請使用反射方式訪問私有方法。





c# unit-testing mocking