web-dev-qa-db-ja.com

ユニットテストのためにC#でファイルシステムをどのようにモックアウトしますか?

ユニットテストを作成するためにC#でファイルシステムをモックアウトするライブラリまたはメソッドはありますか?現在のケースでは、特定のファイルが存在するかどうかを確認し、作成日を読み取るメソッドがあります。将来的にはそれ以上のものが必要になるかもしれません。

136
pupeno

編集:NuGetパッケージをインストールします System.IO.Abstractions

この回答が最初に受け入れられたとき、このパッケージは存在しませんでした。元の答えは、以下の歴史的背景に対して提供されています。

インターフェースを作成することでそれを行うことができます:

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

system.IO.File.Exists()などを使用する「実際の」実装を作成します。その後、モックフレームワークを使用してこのインターフェイスをモックできます。 Moq をお勧めします。

編集:誰かがこれを行って、親切にそれをオンラインで投稿しました ここ

この手法を使用して、DateTime.UtcNowをIClockインターフェイス(時間の流れを制御できるテストで本当に役立ちます!)、およびより伝統的にはISqlDataAccessインターフェイスでモックアウトしました。

別のアプローチは、 TypeMock を使用することです。これにより、クラスへの呼び出しをインターセプトし、それらをスタブアウトできます。ただし、これには費用がかかり、実行するにはチーム全体のPCとビルドサーバーにインストールする必要があります。また、System.IO.Fileでは動作しないようです can ' t stub mscorlib

また、特定のメソッドが単体テスト可能ではないことを受け入れ、別の低速実行統合/システムテストスイートでテストすることもできます。

141
Matt Howells

Install-Package System.IO.Abstractions

このimaginaryライブラリは現在存在します。 System.IO.Abstractions のNuGetパッケージがあり、System.IOを抽象化します名前空間。

テストヘルパーのセット、System.IO.Abstractions.TestingHelpersもあります。これは、執筆時点では部分的にのみ実装されていますが、非常に良い出発点です。

77
Binary Worrier

おそらく、ファイルシステムから必要なものを定義するコントラクトを構築し、それらの機能のラッパーを作成する必要があります。その時点で、実装のモックまたはスタブを作成できます。

例:

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

私の推奨事項は、 http://systemwrapper.codeplex.com/ を使用することです。これは、システム名前空間で最もよく使用される型のラッパーを提供するためです。

5
adeel41

私はこれに対して次の解決策に出くわしました。

  • 単体テストではなく、統合テストを作成します。これが機能するためには、他のテストの干渉を心配することなくものをダンプできるフォルダを作成する簡単な方法が必要です。シンプルな TestFolder クラスがあり、使用するテストメソッドごとに一意のフォルダーを作成できます。
  • モック可能なSystem.IO.Fileを作成します。つまり、 IFile.cs を作成します。私はこれを使うと、しばしばあなたがモック文を書くことができることを証明するテストで終わることに気づきますが、IO使用法が小さい場合にそれを使うべきです。
  • 抽象化のレイヤーを調べて、クラスからファイルIOを抽出します。このためのインターフェースを作成します。残りは統合テストを使用します(ただし、これは非常に小さくなります)。ファイルを読む代わりに、ioThingie.loadSettings()と言うインテントを書く
  • System.IO.Abstractions 。私はまだこれを使用していませんが、それを使って遊ぶのが一番楽しみです。

私が書いているものに応じて、上記のすべての方法を使用することになります。しかし、ほとんどの場合、IOにヒットする単体テストを書くとき、抽象化は間違っていると思うようになります。

インターフェイスを作成してテスト用にモックすることが、最もクリーンな方法です。ただし、代わりに Microsoft Moles フレームワークを見ることができます。

1
Konamiman

特定の質問に答えるには:いいえ、ファイルI/O呼び出しをモックできるライブラリはありません(私が知っている)。つまり、タイプを「適切に」単体テストするには、タイプを定義するときにこの制限を考慮する必要があります。

「適切な」単体テストをどのように定義するかについての簡単な補足説明。ユニットテストでは、既知の入力が提供された場合に、期待される出力(例外、メソッドの呼び出しなど)が得られることを確認する必要があると思います。これにより、ユニットテスト条件を入力および/または入力状態のセットとして設定できます。これを行うための最良の方法は、インターフェイスベースのサービスと依存関係注入を使用して、型の外部の各責任がコンストラクターまたはプロパティを介して渡されるインターフェイスを介して提供されるようにすることです。

だから、これを念頭に置いて、あなたの質問に戻ってください。 mscorlibファイルシステムメソッドの単なるファサードであるIFileSystemService実装と共にFileSystemServiceインターフェイスを作成することにより、ファイルシステム呼び出しをモックしました。私のコードはmscorlibタイプではなくIFileSystemServiceを使用します。これにより、アプリケーションの実行時に標準のFileSystemServiceをプラグインしたり、単体テストでIFileSystemServiceをモックしたりできます。アプリケーションコードは、実行方法に関係なく同じですが、基盤となるインフラストラクチャにより、コードを簡単にテストできます。

Mscorlibファイルシステムオブジェクトのラッパーを使用するのは苦痛ですが、これらの特定のシナリオでは、テストが非常に簡単で信頼性が高くなるため、余分な作業をする価値があります。

1
akmad

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を使用して:

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

ファイルシステムのモックアップ方法がわかりません。できることは、テストに必要な構造を持つフォルダーなどを作成するテストフィクスチャセットアップを記述することです。分解メソッドは、テストの実行後にクリーンアップします。

追加して編集:これについてもう少し考えてみると、このタイプのメソッドをテストするためにファイルシステムをモックしたいとは思わないでしょう。特定のファイルが存在する場合にtrueを返すようにファイルシステムをモックし、そのファイルが存在するかどうかを確認するメソッドのテストでそれを使用する場合、ほとんど何もテストしていません。ファイルシステムのモックが役立つのは、ファイルシステムに依存しているメソッドをテストしたいが、ファイルシステムのアクティビティがテスト中のメソッドに不可欠ではない場合です。

1
Jamie Ide

.NETファイルAPIは実際にはモック可能なインターフェイスまたは拡張可能なクラスに基づいていないため、テストでファイルシステムをモックすることは困難です。

ただし、ファイルシステムにアクセスするための独自の機能層がある場合は、単体テストでそれをモックできます。

モックの代わりに、テストセットアップの一部として必要なフォルダーとファイルを作成し、それらをティアダウンメソッドで削除することを検討してください。

1
LBushkin

一般的なソリューションは、いくつかの抽象的なファイルシステムAPIを使用しています( Apache Commons VFS for Java):すべてのアプリケーションロジックはAPIを使用し、単体テストはスタブ実装で実際のファイルシステムをモックできます) (メモリ内エミュレーションまたはそのようなもの)。

C#の場合、同様のAPIが存在します: NI.Vfs これはApache VFS V1に非常に似ています。ローカルファイルシステムとインメモリファイルシステムの両方のデフォルト実装が含まれています(最後の1つは、ボックスからの単体テストで使用できます)。

0