[英]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的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名稱空間中最常用的類型提供了包裝器
我遇到了以下解決方案:
我最終使用上面的所有方法,具體取決於我在寫什么。 但是大多數時候,當我編寫影響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.Abstractions和System.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.