ユニットテスト用にC#でファイルシステムをモックアウトするにはどうすればよいですか?


149

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


1
これは、stackoverflow.com / questions / 664277 / など、他のいくつかの複製のように見えます。
ジョンサンダース

多分、pex(research.microsoft.com/en-us/projects/pex/filesystem.pdf)を調べてみてください
Tinus

2
@Mitch:ほとんどの場合、データをファイルシステムに配置し、単体テストでコースを実行するだけで十分です。しかし、私は多くのIO操作を実行するメソッドに遭遇し、そのようなメソッドのテスト環境のセットアップは、モックファイルシステムを使用することで大幅に簡略化されました。
スティーブギディ

私はその目的のためにgithub.com/guillaume86/VirtualPath作成しました(それ以上)、それはまだWIPであり、APIは確かに変更されますが、すでに機能し、いくつかのテストが含まれています。
Guillaume86

回答:


154

編集: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とビルドサーバーにインストールする必要があります。また、mscorlibをスタブできないため、System.IO.Fileでは機能しないようです。

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


1
私の意見では、Mattがここで説明するようにインターフェースを作成するのがよい方法です。そのようなインターフェースを生成するツールも作成しました。これは、静的クラスや封印されたクラス、または非決定的なメソッド(つまり、クロックと乱数ジェネレータ)をモックするときに便利です。詳細については、jolt.codeplex.comを参照してください。
スティーブギディ

参照記事のリポジトリが予告なく削除/移動されたようです。ただし、その取り組みのnugetパッケージがここにあるようです:nuget.org/packages/mscorlib-mock
Mike-E

Typemockには、どのタイプが偽造可能かについての制限がありますが、(少なくとも現在のバージョンでは2017年10月現在)File静的クラスを偽造することができます。これを自分で確認しました。
Ryan Rodemoyer 2017年

いくつかの統合テストスイートを要約できますか?
Ozkan

83

インストールパッケージSystem.IO.Abstractions

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

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


3
すでに構築されているこの抽象化を中心に標準化することが最善の策だと思います。そのライブラリについて聞いたことがないので、頭を上げてくれてありがとう。
julealgon 2014

PMはパッケージマネージャーの略です。開く...ツール> NuGetパッケージマネージャー>パッケージマネージャーコンソール
thedanotto '20年

11

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

例:

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

5

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


私は現在このライブラリを使用していますが、FileStreamなどの抽象化にIDisposableが含まれていないことを発見したので、代わりのものを探しています。ライブラリでストリームを適切に破棄できない場合、そのような操作を処理するためにライブラリを推奨(または使用)することはできません。
James Nail

1
SystemWrapperのIFileStreamWrapは、IDisposableを実装しています。
2016

systemwrapperは.netフレームワークのみであり、.netcoreと共に使用すると奇妙な問題が発生します
Adil H. Raza

3

私はこれに対する以下の解決策を見つけました:

  • 単体テストではなく統合テストを記述します。これが機能するためには、他のテストの干渉を心配することなく、データをダンプできるフォルダーを作成する簡単な方法が必要です。使用するテストメソッドごとに一意のフォルダーを作成できる単純なTestFolderクラスがあります。
  • モック可能なSystem.IO.Fileを記述します。つまり、IFile.csを作成します。これを使用すると、モックステートメントを作成できることを証明するだけのテストになってしまうことがよくありますが、IOの使用量が少ないときに使用します。
  • 抽象化の層を調べ、クラスからファイルIOを抽出します。このためのインターフェースを作成します。残りは統合テストを使用します(ただし、これは非常に小さくなります)。これは、ファイルを実行する代わりに、上記とは異なります.ioThingie.loadSettings()などのインテントを書き込みます
  • System.IO.Abstractions。私はまだこれを使用していませんが、これを使って遊ぶのが最も楽しみです。

書いている内容に応じて、上記のすべての方法を使用します。しかし、ほとんどの場合、IOにヒットする単体テストを作成すると、抽象化が間違っていると考えてしまいます。


4
IFile.csへのリンクが壊れています。
Mike-E

3

そのような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);

2

あなたは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
}

1

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

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

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


1

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

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


1

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

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

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

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


1

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


0

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

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


-1

現在、独自のデータエンジンを使用しており、そのAPIはインターフェースとして公開されていないため、データアクセスコードの単体テストはほとんどできません。その後、マットとジョセフのアプローチも取り入れました。


-2

私はジェイミー・イデの反応で行きます。あなたが書いていないことをあざけってはいけません。あなたが知らなかったあらゆる種類の依存関係があります-密封されたクラス、非仮想メソッドなど

別のアプローチは、適切なメソッドをモック可能なものでラップすることです。たとえば、Fileメソッドへのアクセスを許可するFileWrapperというクラスを作成しますが、これはモックアウトできるものです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.