2009-10-06 7 views
50

スタックオーバーフローに関する既存のユニットテスト関連のスレッドを読むと、テストファイルのI/O操作のユニット化方法に関する明確な答えが見つかりませんでした。私は最近、単体テストに目を向け始めました。以前はメリットを認識していましたが、まずテストを書くのに慣れていませんでした。私はNUnitとRhino Mocksを使うプロジェクトを立ち上げましたが、その背後にあるコンセプトを理解していますが、Mockオブジェクトの使い方を理解するのは少し面倒です。ユニットテストファイルI/O

具体的には私は答えたい2つの質問があります。まず、ユニットテストファイルの入出力操作の適切な方法は何ですか?第二に、単体テストについて学ぶ私の試みでは、私は依存性注入を見つけました。 Ninjectをセットアップして動作させた後、単体テストでDIを使うべきか、オブジェクトを直接インスタンス化するべきかどうかは疑問でした。

+5

。 –

+0

DI **の期間**を使用することをお勧めします([この記事](http://blog.ploeh.dk/2011/05/24/PokayokeDesignFromSmellToFragrance.aspx)とそれがリンクしているものを参照してください) –

答えて

28

Tutorial to TDDを確認するには、Rhino MocksSystemWrapperを使用してください。

SystemWrapperは、File、FileInfo、Directory、DirectoryInfoなどのSystem.IOクラスの多くをラップします。 the complete listが表示されます。

このチュートリアルでは、MbUnitでのテスト方法を示していますが、NUnitとまったく同じです。あなたがここに3つのオプションがあり

[Test] 
public void When_try_to_create_directory_that_already_exists_return_false() 
{ 
    var directoryInfoStub = MockRepository.GenerateStub<IDirectoryInfoWrap>(); 
    directoryInfoStub.Stub(x => x.Exists).Return(true); 
    Assert.AreEqual(false, new DirectoryInfoSample().TryToCreateDirectory(directoryInfoStub)); 

    directoryInfoStub.AssertWasNotCalled(x => x.Create()); 
} 
+3

+1 - SystemWrapperは便利です!ポッドキャストにはこのテクニックの素晴らしい議論があります。http://www.slickthought.net/post/2009/04/03/New-Spaghetti-Code-Podcast-ndash3b-Donn-Felker-Talks-Mocking.aspx – TrueWill

+0

これはまだNuGetで利用できますか?それを見ていない。私はちょうどそれを逃したことを受け入れることを望んでいます:) –

+0

それはNuGetで利用可能です。最新バージョンではありませんが、利用可能です。 SystemWrapperを検索します。 https://nuget.org/packages/SystemWrapper/0.4 – Vadim

9

Q1:

あなたのテストでは、このような何かを見に行くされています。

オプション1:それと一緒に生きてください。

(NO例:P)

オプション2:必要なわずかな抽象化を作成します。

テスト中のメソッドでファイルI/O(File.ReadAllBytesなど)を実行する代わりに、IOが外部で実行され、代わりにストリームが渡されるように変更できます。

public class MyClassThatOpensFiles 
{ 
    public bool IsDataValid(string filename) 
    { 
     var filebytes = File.ReadAllBytes(filename); 
     DoSomethingWithFile(fileBytes); 
    } 
} 

は、このアプローチは、トレードオフである

// File IO is done outside prior to this call, so in the level 
// above the caller would open a file and pass in the stream 
public class MyClassThatNoLongerOpensFiles 
{ 
    public bool IsDataValid(Stream stream) // or byte[] 
    { 
     DoSomethingWithStreamInstead(stream); // can be a memorystream in tests 
    } 
} 

なります。まず、はい、それはもっとテスト可能です。しかし、それはテスト容易性と僅かな複雑さを加えています。メンテナンス性とコードの書き方を損なうことがあります。さらに、テストの問題を1段階上に進めることもできます。

私の経験では、完全にラップされたファイルシステムにコミットせずに、重要なロジックを一般化してテスト可能にすることができます。私。残りの部分をそのまま残しながら、本当に気にしているビットを一般化することができます。

オプション3:ステップさらにそれを取っ

ファイルシステム全体をラップし、ファイルシステムをモックする有効なアプローチであることができます。それはあなたがどれほど鼓動しているかによって決まります。

私はこれまでにこのルートを行ってきました。私はラップされたファイルシステムの実装を持っていましたが、結局私はそれを削除しました。APIに微妙な違いがありました。私はどこにでも注入しなければならず、最終的には、それを使用するクラスの多くが私にとって非常に重要ではなかったので、少しの利益のために余分な痛みでした。私がIoCコンテナを使用していたり​​、クリティカルなものを書いていて、テストが速くなければならなかった場合、私はそれに固執していたかもしれません。これらすべてのオプションと同様に、あなたの走行距離は異なる場合があります。あなたのIoCコンテナの質問については

手動テストダブルを注入します。反復作業を頻繁にやる必要がある場合は、テストでsetup/factoryメソッドを使用してください。 IoCコンテナをテスト用に使用することは、極端な場合には過剰です。おそらく私はあなたの2番目の質問を理解していないでしょう。

1

現在、私は依存関係注入によってIFileSystemオブジェクトを消費しています。プロダクションコードでは、ラッパークラスが必要な特定のIO関数をラップするインターフェイスを実装します。テスト時には、nullまたはスタブの実装を作成してテスト中のクラスに提供することができます。テストされたクラスは賢明ではありません。

32

ファイルシステムをテストするときにはがあります。実際には、状況に応じていくつかのことがあります。

質問する必要があります質問:私は何をテストしていますか?ファイルシステムが動作することを

  • 非常によく知られていないオペレーティングシステムを使用している場合を除いて、をテストする必要はないでしょう。たとえば、単にファイルを保存するコマンドを与えているのであれば、実際に保存するためのテストを書くのは時間の無駄です。

  • ファイルは適切な場所に保存されますか?さて、あなたはどのような場所が正しいかをどのように知っていますか?パスとファイル名を組み合わせたコードがあると考えられます。これは簡単にテストできるコードです:あなたの入力は2つの文字列であり、出力はその2つの文字列を使って構築された有効なファイル位置である文字列でなければなりません。

  • ディレクトリから適切なファイルセットを取得できたとしますか?おそらくファイルシステムを実際にテストするファイルゲッタークラスのテストを書く必要があります。しかし、変更されないファイルが入ったテストディレクトリを使うべきです。このテストは、ファイルシステムに依存するため、実際の単体テストではないため、統合テストプロジェクトに配置する必要があります。

  • しかし、私が得るファイルで何かする必要があります。の場合、テストの場合は、ファイルゲッタークラスにの偽物を使用する必要があります。あなたの偽造品は、ハードコードされたファイルのリストを返すべきです。 リアルファイルゲッターとリアルファイルプロセッサを使用すると、どちらがテストに失敗するのか分かりません。だから、あなたのファイルプロセッサクラスは、テストでは、偽のファイルゲッタークラスを利用するべきです。あなたのファイルプロセッサクラスは、ファイル取得者インタフェースを取る必要があります。実際のコードでは、実際のファイルゲッターを渡します。テストコードでは、既知の静的リストを返す偽のファイル取得メソッドを渡します。

基本原則は以下のとおりです。あなたは、ファイルシステム自体をテストしていないとき

  • は、インターフェイスの後ろに隠れて偽のファイルシステムを使用してください。
  • 実際のファイル操作をテストする必要がある場合は、
    • という単位テストではなく、統合テストとしてマークします。
    • には、指定されたテストディレクトリ、ファイルのセットなどがあり、それらは常に変更されていない状態で存在するため、ファイル指向の統合テストに一貫して合格することができます。
1

は、2012年以来、あなたはそれがすでに凍結されたために必要としないMicrosoft Fakesを使用すると、たとえば、あなたのコードベースを変更することを行うことができます。

System.dllのためgenerate a fake assemblyまず - または任意の他のパッケージとその後のように期待収益を模擬:ユニットテストのためのDIを使用する別の質問ihmoなければならないか否か

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 
}