すぐに実行されない単体テストコード
-
06-07-2019 - |
質問
C#3.0とNUnitを使用しています。しばらくして実行されるコードで単体テストを実行する標準的な方法があるかどうか疑問に思っています。たとえば、メソッドを登録できる単純な静的クラスがあり、nミリ秒後に呼び出されるようにします。デリゲートメソッドのコードが呼び出されることを保証する必要があります。
たとえば、メソッドが終了するまでアサーションは行われないため、次のテストは常に合格します。
[Test]
public void SampleTest()
{
IntervalManager.SetTimeout(delegate{
Assert.Equals(now.Millisecond + 100, DateTime.Now.Millisecond);
}, 100);
}
すぐに実行されないコードを単体テストすることも可能ですか?
乾杯、
ポール
解決
これはどうですか?これにより、テストは、コールバックが発火して完了するまでの予想最大時間の間ブロックされ、終了する前にエラーを報告します。
public void Foo() {
AutoResetEvent evt = new AutoResetEvent(false);
Timer t = new Timer(state => {
// Do work
evt.Set();
}, null, 100, Timeout.Infinite);
if (evt.WaitOne(500)) {
// method called and completed
} else {
// timed out waiting
}
}
他のヒント
では、正確に何をテストしていますか?タイマーが機能することをテストしていますか?または、コードがタイマーを適切に設定して、期限切れ時にタイマーがコールバックを実行するようにしますか?コードがどのように見えるかを知らずに、私はあなたが本当にテストしたいのは後者だと仮定しています。私の答えは(1)これはおそらく静的メソッドでは難しいでしょう、そして(2)依存性注入を使用し、実際には結果のメソッドを実行せずに記録するモックタイマーなどを注入する必要があるでしょう適切な呼び出しがコードによって行われたという期待を介して。
補足として。通常、低速のテストは NUnitカテゴリでマークし、一部のビルドでこれらのテストをスキップすることを選択できます。
はい、例のように実行しても機能しません。
むしろ、デリゲートとして使用するテストクラスを作成し、そのメソッドがいつ、いつ呼び出されたかを記録することをお勧めします。
次に、テストするIntervalManagerにモックを挿入します。次に、テストメソッドはIntervalManagerを待機する必要があり(IntervalManagerが提供する適切なメソッドを使用するか、数秒待つだけです)、テストクラスの状態を確認できます。
ところで、このアプローチは通常モッキングと呼ばれます。この場合、テストクラスはモックオブジェクトになります。
他のいくつかの答えが示しているように、長時間のテストは一般に悪い考えです。このコンポーネントのテストを容易にするために、テストしようとしているものが2つあることを考慮してください。
- 時限デリゲートの実行を登録すると、適切な時間が設定されます。これは、タイムアウトとデリゲートの数のさまざまな組み合わせでテストする必要があります。
- デリゲートは適切な方法で実行されます。
この方法でテストを分離すると、少数の短いタイムアウトでタイミングメカニズムが期待どおりに動作することをテストできます(考慮する必要があるすべてのケースをテストします)。システムの現在の負荷と、コンポーネントコードの複雑さ( IntervalManager
)。
もちろん、テストできます。実行するのを待つだけです。
たぶん何かが足りないかもしれませんが、Visual Studioのユニットテストには、実行順序などを制御するメソッドに追加できる特別な属性があります。これは、ユニットテストプロジェクトを最初に作成したときに自動的に生成されている必要があります。
#region Additional test attributes
//
//You can use the following additional attributes as you write your tests:
//
//Use ClassInitialize to run code before running the first test in the class
//[ClassInitialize]
//public static void MyClassInitialize(TestContext testContext) {
//}
//
//Use ClassCleanup to run code after all tests in a class have run
//[ClassCleanup()]
//public static void MyClassCleanup()
//{
//}
//
//Use TestInitialize to run code before running each test
//[TestInitialize()]
//public void MyTestInitialize()
//{
//}
//
//Use TestCleanup to run code after each test has run
//[TestCleanup()]
//public void MyTestCleanup()
//{
//}
//
#endregion
[ClassInitialize]を使用すると、最初に実行する必要のあるものをメソッドに書き込むことができます。その後、テストを実行できます。または、[TestInitialize]を使用して、各テストの前にコードを実行できます。