セットアップ:
_public interface IFoo
{
void Fizz();
}
[Test]
public void A()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Object.Fizz();
foo.Verify(x => x.Fizz());
// stuff here
foo.Verify(x => x.Fizz(), Times.Never()); // currently this fails
}
_
基本的に、foo.Verify(x => x.Fizz(), Times.Never())
を渡すために_// stuff here
_にコードを入力したいと思います。
そして、これはおそらくmoq/unit testingの乱用を構成するので、私の正当化は次のようなことができるようにすることです:
_[Test]
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
// reset the verification here
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
_
基本的に、Stateオブジェクトをプッシュするには、かなりの量の作業(さまざまなモックオブジェクトの作成とその他の処理)が必要な状態オブジェクトがあります。次に、State1からState2への移行をテストします。コードを複製または抽象化する代わりに、State1テストを再利用し、State2にプッシュしてアサートを実行します。検証呼び出し以外はすべて実行できます。
このようなモックをリセットできるとは思わない。代わりに、状態1に移行するときにFizz
を1回呼び出す必要があることがわかっている場合は、次のように検証できます。
objectUnderTest.DoStuffToPushIntoState1();
foo.Verify(x => x.Fizz(), Times.Once()); // or however many times you expect it to be called
objectUnderTest.DoStuffToPushIntoState2();
foo.Verify(x => x.Fizz(), Times.Once());
そうは言っても、このために2つの個別のテストを作成します。 2つのテストとして、状態1への遷移が失敗しているか、状態2への遷移が失敗しているかを簡単に確認できます。さらに、このように一緒にテストすると、状態1への移行が失敗した場合、テストメソッドは終了し、状態2への移行はテストされません。
編集
この例として、xUnitで次のコードをテストしました。
[Fact]
public void Test()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Object.Fizz();
foo.Verify(x => x.Fizz(), Times.Once(), "Failed After State 1");
// stuff here
foo.Object.Fizz();
foo.Verify(x => x.Fizz(), Times.Once(), "Failed after State 2");
}
このテストは、「状態2の後で失敗しました」というメッセージで失敗します。これは、fooをState 2にプッシュするメソッドがFizz
を呼び出すとどうなるかをシミュレートしています。存在する場合、2番目のVerify
は失敗します。
コードをもう一度見ると、1つのメソッドを呼び出してそれがモックの別のメソッドを呼び出すかどうかを確認するため、CallBase
をtrue
に設定して、ベースDoStuffToPushIntoState2
は、モックのオーバーライドではなく呼び出されます。
この投稿が作成されてからずっと後に、OPが要求していた機能が追加されたと思います。Moq.MockExtensions.ResetCalls()というMoq拡張メソッドがあります。
この方法を使用すると、次のように希望どおりに実行できます。
[Test]
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
foo.ResetCalls(); // *** Reset the verification here with this glorious method ***
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
更新
.ResetCalls()の代わりに、ライブラリの最新バージョンで.Invocations.Clear()を使用する必要があります。
foo.Invocations.Clear()
また、MoQを使用したユニットテストでTimes.Exactly(1)検証エラーが発生し、「was called 2 times」エラーメッセージが表示されました。私はこれをMoQのバグと考えています。テストを実行するたびにクリーンなモック状態が期待されるからです。
私の回避策は、テストセットアップで新しいモックインスタンスとテストターゲットを割り当てることでした。
private Mock<IEntityMapper> entityMapperMock;
private OverdraftReportMapper target;
[SetUp]
public void TestSetUp()
{
entityMapperMock = new Mock<IEntityMapper>();
target = new OverdraftReportMapper(entityMapperMock.Object);
}
Verifyの代わりにCallbackメソッドを使用して、呼び出しをカウントできます。
これは Moqクイックスタートページ で示されているため、次のようになります。
// returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => calls)
.Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.GetCountThing());
使用しているMockのバージョンによって異なりますが、これができることは確かです
someMockObject.ResetCalls();
1つのテストで2つのことを確認しているため、これは確かに単体テストの悪用です。テストからObjectUnderTest
の初期化を取り除いて、一般的なセットアップ方法にすれば、あなたの人生はずっと楽になります。その後、テストはより読みやすくなり、相互に独立します。
実動コードよりも、テストコードを読みやすく分離するために最適化する必要があります。システムの動作の1つの側面のテストは、他の側面に影響を与えるべきではありません。モックオブジェクトをリセットするよりも、一般的なコードをセットアップメソッドにリファクタリングする方が、はるかに簡単です。
ObjectUnderTest _objectUnderTest;
[Setup] //Gets run before each test
public void Setup() {
var foo = new Mock<IFoo>(); //moq by default creates loose mocks
_objectUnderTest = new ObjectUnderTest(foo.Object);
}
[Test]
public void DoStuffToPushIntoState1ShouldCallFizz() {
_objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
}
[Test]
public void DoStuffToPushIntoState2ShouldntCallFizz() {
{
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
@stackunderflowによる回答への追加(笑、ニースニック:))
Moqの以降のバージョンでは、Moq.MockExtensions.ResetCalls()
は廃止されたとマークされていました。代わりにmock.Invocations.Clear()
を使用する必要があります。
foo.Invocations.Clear();
次のアプローチは私にとってはうまくいきます(Moq.Sequenceを使用)
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
// Some cool stuff
using (Sequence.Create())
{
foo.Setup(x => x.Fizz()).InSequence(Times.Never())
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
}
}
うまくいったかどうか教えてください