ユニットテストを実行することを強くお勧めします ファイルシステムから分離して、テストでファイルシステムに触れると、ファイルシステム自体もテストするためです。 OK、それは合理的です。
私の質問は、ディスクへのファイル保存をテストしたい場合、どうすればよいですか?データベースと同様に、データベースアクセスを担当するインターフェイスを分離してから、テスト用にこれの別の実装を作成しますか?それとも他の方法があるのでしょうか?
これに対する私のアプローチは、私が今読んだばかりの Growing Object-Oriented Software Guided by Tests (GOOS)の本に大きく偏っていますが、それは私が今日知っている中で最高です。具体的には:
コメント投稿者向けの更新:
構造化データ(ブックオブジェクトなど)を読んでいる場合(IEnumerableの代わりに文字列を使用しない場合)
interface BookRepository
{
IEnumerable<Books> LoadFrom(string filePath);
void SaveTo(string filePath, IEnumerable<Books> books);
}
これで、constructor-injectionを使用して、クライアントクラスにモックを注入できます。したがって、クライアントクラスユニットテストは高速です。 ファイルシステムにヒットしない。依存関係で適切なメソッドが呼び出されていることを確認するだけです(例:ロード/保存)
var testSubject = new Client(new Mock<BookRepository>.Object);
次に、ファイル(または、必要に応じて明日SQL DB)で機能するBookRepositoryの実際の実装を作成する必要があります。他の誰も知る必要はありません。 FileBasedBookRepository(上記のロールを実装する)の統合テストを記述し、参照ファイルを使用してLoadを呼び出すと適切なオブジェクトが得られ、既知のリストを使用してSaveを呼び出すと、それらがディスクに保持されることをテストします。 。つまり、実際のファイルを使用しますこれらのテストは遅いので、タグでマークアップするか、別のスイートに移動します。
[TestFixture]
[Category("Integration - Slow")]
public class FileBasedBookRepository
{
[Test]
public void CanLoadBooksFromFileOnDisk() {...}
[Test]
public void CanWriteBooksToFileOnDisk() {...}
}
最後に、ロードと保存を実行する1つ以上の受け入れテストがあるはずです。
保存関数にファイル名を渡す代わりに、Stream、TextWriterなどを渡すことができます。次に、テスト時にメモリベースの実装に合格し、実際にディスクに何も書き込まずに正しいバイトが書き込まれていることを確認できます。
問題と例外をテストするために、モックフレームワークを見ることができます。これは、保存プロセスの特定の時点で特定の例外を人為的に生成し、コードがそれを適切に処理することをテストするのに役立ちます。
ファイルI/Oを実行する単体テストは速度が遅すぎる傾向があるため、これらの単体テストの作成には注意が必要です。ただし、単体テストでのファイルI/Oの絶対的な禁止はありません。
単体テストでは、一時ディレクトリを設定および破棄し、その一時ディレクトリ内にテストファイルとディレクトリを作成します。はい、テストは純粋なCPUテストよりも遅くなりますが、それでも高速です。 JUnitには、まさにこのシナリオを支援するサポートコードもあります:a @Rule
TemporaryFolder
。
そうは言っても、ほとんどのファイル書き込みコードは次の形式を取ります。
最初のものだけが実際にファイルシステムを扱います。残りは出力ストリームを使用するだけです。
したがって、名前付きファイルではなく、特定の出力ストリームを操作する独自のメソッド(関数)に中間部分と最後の部分を抽出できます。次に、その出力ストリームをモックして、メソッドの単体テストを行うことができます。これらの単体テストは非常に高速になります。ほとんどのプログラミング言語は、すでに適切な出力ストリームクラスを提供しています。
それはユニットテストされる最初の部分だけを残します。必要なテストはわずかなので、テストスイートは許容できるほど高速である必要があります。