簡體   English   中英

您如何在C#中模擬文件系統以進行單元測試?

[英]How do you mock out the file system in C# for unit testing?

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

編輯:安裝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進行存根處理

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

安裝包System.IO.Abstractions

這個虛構的庫現已存在,其中有一個用於System.IO.Abstractions的NuGet程序包,該程序包抽象了System.IO命名空間。

還有一組測試幫助程序System.IO.Abstractions.TestingHelpers-在編寫本文時-僅部分實現,但這是一個很好的起點。

您可能必須建立一個合同來定義文件系統需要什么,然后圍繞這些功能編寫包裝器。 到那時,您將可以模擬或存根實現。

例:

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"); }
}

我的建議是使用http://systemwrapper.codeplex.com/,因為它為System名稱空間中最常用的類型提供了包裝器

我遇到了以下解決方案:

  • 編寫集成測試,而不是單元測試。 為此,您需要一種簡單的方法來創建一個文件夾,您可以在其中轉儲內容而不必擔心其他測試的干擾。 我有一個簡單的TestFolder類,可以為每個測試方法文件夾創建一個唯一的文件夾。
  • 編寫一個可模擬的System.IO.File。 那就是創建一個IFile.cs 我發現使用它的結果通常是測試,這些測試僅證明您可以編寫模擬語句,但是在IO使用量較小時才使用它。
  • 檢查您的抽象層,並從類中提取文件IO。 為此創建一個接口。 其余的使用集成測試(但這將很小)。 這與上面的不同之處在於它不是執行文件操作。您要寫意圖,例如ioThingie.loadSettings()
  • System.IO.Abstractions 我還沒有使用過,但是這是我最興奮的玩法。

我最終使用上面的所有方法,具體取決於我在寫什么。 但是大多數時候,當我編寫影響IO的單元測試時,我總是以為抽象是錯誤的。

我不確定您將如何模擬文件系統。 您可以做的是編寫一個測試夾具設置,該創建一個具有測試所需結構的文件夾等。 測試運行后,拆解方法將清除它。

編輯添加:在多考慮這一點時,我認為您不想模擬文件系統來測試這種類型的方法。 如果您模擬文件系統是否存在某個特定文件並返回true,並在測試該文件是否存在的方法的測試中使用該文件系統,則您將不需要進行任何測試。 如果您想測試依賴於文件系統的方法,但是文件系統活動不是被測試方法所不可或缺的,那么模擬文件系統將很有用。

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

但是,如果您擁有自己的功能層來訪問文件系統,則可以在單元測試中對其進行模擬。

作為模擬的替代方法,請考慮僅在測試設置中創建所需的文件夾和文件,然后在拆卸方法中將其刪除。

要回答您的特定問題:不,沒有庫可讓您模擬文件I / O調用(據我所知)。 這意味着“正確地”對類型進行單元測試將要求您在定義類型時考慮此限制。

關於如何定義“適當的”單元測試的簡要說明。 我認為,單元測試應確認您提供了已知的輸入,從而獲得了預期的輸出(例如,異常,方法調用等)。 這使您可以將單元測試條件設置為一組輸入和/或輸入狀態。 我發現執行此操作的最佳方法是使用基於接口的服務和依賴項注入,以便通過構造函數或屬性傳遞的接口來提供類型外部的每個職責。

因此,考慮到這一點,請回到您的問題。 我通過創建IFileSystemService接口以及FileSystemService實現(它只是mscorlib文件系統方法的基礎)來模擬文件系統調用。 然后,我的代碼使用IFileSystemService而不是mscorlib類型。 這使我可以在應用程序運行時插入標准FileSystemService或在單元測試中模擬IFileSystemService 無論應用程序代碼如何運行,該應用程序代碼都是相同的,但是底層基礎結構允許對該代碼進行輕松測試。

我會承認,在mscorlib文件系統對象周圍使用包裝器是很痛苦的,但是,在這些特定情況下,由於測試變得更加容易和可靠,因此值得進行額外的工作。

創建接口並對其進行模擬以進行測試是最干凈的方法。 但是,作為替代方案,您可以看看Microsoft Moles框架。

您可以使用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
}

通過像這樣使用System.IO.AbstractionsSystem.IO.Abstractions.TestingHelpers

public class ManageFile {
   private readonly IFileSystem _fileSystem;
   public ManageFile(IFileSystem fileSystem){

      _fileSystem = fileSystem;
   }

   public bool FileExists(string filePath){}
       if(_fileSystem.File.Exists(filePath){
          return true;
       }
       return false;
   }
}

在測試類中,使用MockFileSystem()模擬文件,然后實例化ManageFile,如下所示:

var mockFileSysteme = new MockFileSystem();
var mockFileData = new MockFileData("File content");
mockFileSysteme.AddFile(mockFilePath, mockFileData );
var manageFile = new ManageFile(mockFileSysteme);

常見的解決方案是使用一些抽象的文件系統API(例如Apache Commons VFS for Java):所有應用程序邏輯都使用API​​,並且單元測試能夠通過存根實現(內存中仿真或類似的東西)模擬真實的文件系統。

對於C#,存在類似的API: NI.Vfs ,與Apache VFS V1非常相似。 它包含本地文件系統和內存文件系統的默認實現(可以在框內的單元測試中使用最后一個)。

當前,我們使用專有的數據引擎,並且其API並未公開為接口,因此我們幾乎無法對數據訪問代碼進行單元測試。 然后我也接受了馬特和約瑟夫的方法。

我同意傑米·艾德的回答。 不要試圖嘲笑您沒有寫的東西。 您將不知道有各種各樣的依賴關系-密封類,非虛擬方法等。

另一種方法是用一些可模擬的東西來包裝appopiate方法。 例如,創建一個名為FileWrapper的類,該類允許訪問File方法,但是您可以模擬一下。

暫無
暫無

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

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