ビデオ ファイルのマルチスレッド処理を行うクラスを単体テストしようとしています。テスト対象のクラスは ParallelTranscoder です。
ParallelTranscoder は、ファクトリに依存して、キュー内の各項目の新しいトランスコーダを作成します。そこで、スタブ IAsyncTranscoder とスタブ IAsyncTranscoderFactory を作成しています。次に、transcoderFactoryStub.Create() が呼び出されたときに渡されるように、transcoderStub をファクトリにフィードします。
IAsyncTranscoder は、処理が終了したときにイベントを発生させる必要があるため、スタブは、TranscodeAsync() が呼び出されるとすぐに FinishedEvent を発生させるように設定されています。
これらはすべて、1 つの項目のみをキューに入れる最初のテスト メソッドで機能しています。ただし、2 番目のテスト メソッドでは、次のメッセージで失敗します。
System.AggregateException : 1 つ以上のエラーが発生しました。
----> System.AggregateException : 1 つ以上のエラーが発生しました。
----> System.InvalidOperationException : モック オブジェクトが再生状態の場合、このアクションは無効です。
例外をスローする行はtranscoderStub.Raise(t => t.FinishedEvent += null,...
、CreateTranscoderStub() メソッド内にあるようです。
これは、Rhino Mocks がスレッド セーフではない可能性があるためですか、それとも単に間違っているのでしょうか? (私はRhino Mocksが初めてです)
原因に関係なく、これを行う方法はありますか?
[TestFixture]
class ParallelTranscoderTests {
[Test]
public void Transcode_FinishWithoutError_RaisesFinishedEvent() {
var transcoderStub = CreateTranscoderStub();
var transcoderFactoryStub = MockRepository.GenerateStub<IAsyncTranscoderFactory>();
transcoderFactoryStub.Stub(stub => stub.Create()).Return(transcoderStub);
bool transcodingFinishedEventWasRaised = false;
var transcoderUnderTest = new ParallelTranscoder(concurrencyLevel: -1, transcoderFactory: transcoderFactoryStub);
// Subscribe to the event and set a flag if it's called
transcoderUnderTest.FinishedEvent += (sender, args) => { transcodingFinishedEventWasRaised = true; };
transcoderUnderTest.EnqueueTranscodeItem(new TranscodeItem());
transcoderUnderTest.Transcode();
Assert.That(transcoderUnderTest.TranscodingFinished, Is.True);
Assert.That(transcodingFinishedEventWasRaised, Is.True);
}
[Test]
public void Transcode_TwoItemsFinishWithoutError_RaisesItemFinishedEventTwiceAndFinishedEventOnce() {
var transcoderStub1 = CreateTranscoderStub();
var transcoderStub2 = CreateTranscoderStub();
var transcoderFactoryStub = MockRepository.GenerateStub<IAsyncTranscoderFactory>();
transcoderFactoryStub.Stub(stub => stub.Create()).Return(transcoderStub1).Repeat.Once();
transcoderFactoryStub.Stub(stub => stub.Create()).Return(transcoderStub2).Repeat.Once();
int timesTranscodingFinishedEventWasRaised = 0;
int timesItemFinishedEventWasRaised = 0;
var transcoderUnderTest = new ParallelTranscoder(
concurrencyLevel: -1,
transcoderFactory: transcoderFactoryStub
);
transcoderUnderTest.FinishedEvent += (sender, args) => {
timesTranscodingFinishedEventWasRaised++;
};
transcoderUnderTest.ItemFinishedEvent += (sender, args) => {
timesItemFinishedEventWasRaised++;
};
transcoderUnderTest.EnqueueTranscodeItem(new TranscodeItem(){InFile = "item1_in", OutFile = "item1_out"});
transcoderUnderTest.EnqueueTranscodeItem(new TranscodeItem(){InFile = "item2_in", OutFile = "item2_out"});
transcoderUnderTest.Transcode();
Assert.That(transcoderUnderTest.TranscodingFinished, Is.True);
Assert.That(timesTranscodingFinishedEventWasRaised, Is.EqualTo(1));
Assert.That(timesItemFinishedEventWasRaised, Is.EqualTo(2));
}
private static IAsyncTranscoder CreateTranscoderStub() {
var transcoderStub = MockRepository.GenerateStub<IAsyncTranscoder>();
transcoderStub.Stub(stub => stub.TranscodingFinished).Return(true);
transcoderStub.Stub(stub => stub.TranscodeAsync()).WhenCalled(delegate {
transcoderStub.Raise(t => t.FinishedEvent += null,
transcoderStub,
new TranscoderFinishedEventArgs
(true, DateTime.Now,
DateTime.Now, 0));
});
return transcoderStub;
}
}