操作手順
先ほど確認していたClass1Test.csファイルを開きます。このファイルの35行目に追加のテスト属性という記述のブロック要素があるので、これを展開すると図1のようになります。
ここには4つのメソッドがコメントアウトされています。このうち、MyTestInitializeメソッドとMyTestCleanupメソッドのコメントアウトを解除します。解除後の状態は図2のようになります。
次に、コメントアウトを解除した各メソッドにテストの前処理と後処理を記述します。MyTestInitializeメソッドにはReadFileTest1メソッドでテストの準備と書いてあるブロックを記述します。ただし、そのまま転記すると後続の作業に少し都合が悪いので、多少の書き換えを行い、フィールド変数を1つ追加します。記述後の状態はリスト2のようになります。
//フィールド変数の追加 priavate string writePath; [TestInitialize()] public void MyTestInitialize() { string testPath = this.TestContext.DeploymentDirectory; this.writePath = Path.Combine(testPath, "readtest1.txt"); File.WriteAllText(writePath, "あいうえお\nかきくけこ\nさしすせそ", Encoding.Default); }
続いて、MyTestCleanupメソッドには、ReadFileTest1メソッドでテストの後片付けと書いてあるブロックを記述します。ただし、そのままではコンパイルエラーになるので、少しコードの編集を行います。記述後の状態はリスト3のようになります。
[TestCleanup()] public void MyTestCleanup() { File.Delete(this.writePath); }
MyTestInitializeメソッドとMyTestCleanupメソッドの作成が終了したら最後に、ReadFileTest1メソッドに記述されているテストの準備処理とテストの後片付けの処理を削除します。ここもMyTestCleanupメソッドと同様にそのままではコンパイルエラーになるので、少しコードの編集を行います。編集後の状態はリスト4のようになります。
[TestMethod()] public void ReadFileTest1() { //テストの実行 Class1 target = new Class1(); string[] expected = new string[] { "あいうえお", "かきくけこ", "さしすせそ" }; string filePath = this.writePath; string[] actual; actual = target.ReadFile(filePath); Assert.AreEqual(expected.Length, actual.Length); Assert.AreEqual(expected[0], actual[0]); Assert.AreEqual(expected[1], actual[1]); Assert.AreEqual(expected[2], actual[2]); }
もう1つのReadFileTest2メソッドでも同様の手順によって必要なくなったコードを削除して編集終了です。最後にテストを実行して編集前同様にテストが成功することを確認しましょう。
補足や注意事項
以上でVisual Studioの単体テストフレームワークに用意されているテストの前処理、後処理を実行する仕組みを利用したテストの実施を行うところまでを見てきましたが、何点か補足と注意事項を確認しておきましょう。
まず、今回利用したMyTestInitializeメソッドとMyTestCleanupメソッドですが、この2つのメソッドにはそれぞれ[TestInitialize]属性と[TestCleanup]属性が付与されています。Visual Studioの単体テストフレームワークは各単体テストメソッドを実行する際に"同じクラス内にTestInitialize属性が付与されたメソッドがあれば"テストメソッドを呼び出す前に該当するメソッドを呼び出します。同様にテストメソッド終了後に"同じクラス内にTestCleanup属性が付与されたメソッドがあれば"該当するメソッドを呼び出してテストを完了させます。この動きをテストメソッドごとに繰り返していきます。図で表現すると図3のようになります。
このような動作のため、各テストメソッドに前処理、後処理を書くことなく1か所に記述するだけで簡単に再利用を行うことができます。
次に、TestInitialize属性、TestCleanup属性は各クラスに1つのみ記述することができます。1つのクラス内にこれらの属性が2つ以上重複して定義されている場合、テストを実行する際に警告がでてテストが実行されません。もし、異なる初期化処理を行いたい場合には、単体テストメソッドを定義するクラスを分離したのちにそれぞれのクラスでTestInitialize, TestCleanupを定義するようにします。
最後に、その他にも用意されている初期化、クリーンアップ処理があります。具体的には、表1の4種類が用意されています。
属性名 | 説明 |
AssemblyInitialize | アセンブリがロードされた直後に実行されます。 |
AssemblyCleanup | アセンブリがアンロードされる直前に実行されます。 |
ClassInitialize | テストクラスのインスタンスが作成された直後に実行されます。 |
ClassCleanup | テストクラスのインスタンスが解放される直前に実行されます。 |
例えば、初期化やクリーンアップで時間のかかる処理を行う必要がある場合、TestInitializeやTestCleanupでその処理を行うと、テストごとに行われるため、テスト時間が非常にかかってしまうことになります。可能な場合にはそれらをClassInitializeやAssemblyInitializeなど、より呼び出し回数の少ない部分に移動することでテスト時間を短縮することが可能となります。これら全てのメソッドの実行の流れは図4のようになります。
ただし、AssemblyCleanupやClassCleanupのメソッドが呼び出されるタイミングが、アセンブリに含まれる全てのテストメソッドが実行された直後やクラスに含まれる全てのテストメソッドが実行された直後ではない点には注意してください。呼び出しタイミングの都合によりほとんどの場合、他のクラスや他のアセンブリのテストと排他を取るためにAssemblyCleanup, ClassCleanupを利用することはできません。