web-dev-qa-db-ja.com

Moq-セットアップで検証基準(呼び出し時間など)を指定することは可能ですか?

戻り値を設定し、式が呼び出された回数を確認する必要がある場合、これを1つのステートメントで実行できますか?

私が収集できるものから、MoqのSetup(SomeExpression).Verifiable()Verify()とともに呼び出され、基本的にVerify(SomeExpression, Times.AtLeastOnce)を実行しますか?つまり、式が呼び出されたことのみを確認します。

これは質問をよりよく説明するための例です。インターフェースの場合:

interface IFoo
{
    int ReturnSomething();
}

次の2つのブロックは同等ですか(最初のもの以外は、検証可能としてマークされたすべてのセットアップを検証します)?

void Test()
{
    var mock = new Mock<IFoo>();
    mock.Setup((m) => m.ReturnSomething()).Returns(1).Verifiable();

    mock.Verify();
}

そして

void Test()
{
    var mock = new Mock<IFoo>();
    mock.Setup((m) => m.ReturnSomething()).Returns(1);

    mock.Verify((m) => m.ReturnSomething(), Times.AtLeastOnce());
}

呼び出しの回数を確認したい場合(たとえば2回)、これが唯一の方法であり、セットアップと確認で式が繰り返されますか?

void Test()
{
    var mock = new Mock<IFoo>();
    mock.Setup((m) => m.ReturnSomething()).Returns(1);

    mock.Verify((m) => m.ReturnSomething(), Times.Exactly(2));
}

SetupとVerifyを呼び出す必要がないだけです。まあ、これはAAAにとって良い考えなので、言い換えると、セットアップと確認のために式を繰り返す必要がありません。現時点では、式を変数に格納して各メソッドに渡しますが、それほどクリーンではありません。

PS-これのコンテキストは、キャッシュが更新されたかどうか(有効期限など)をチェックするテスト用です。

35
GregS

私はいつもこの問題を抱えています。私は厳密なモックを使用しており、厳密に指定したい(つまり、It.Is<>()ではなくIt.IsAny()を使用した)とともに、厳密に検証したい(つまり、時間を指定したい)。 MoqにはVerifiable(Times)オーバーロードが欠落しているため、悲しいことにverifiableを使用することはできません。

It.Is<>()を含む呼び出しの完全な式は、一般的に大きくなります。重複を避けるために、私は一般的に次のことに頼っています:

Expression<Action<MockedType>> expression = mockedTypeInstance => mockedTypeInstance.MockedMethod(It.Is<TFirstArgument>(firstArgument => <some complex statement>)/*, ...*/);
_mock.Setup(expression);

/* run the test*/

_mock.Verify(expression, Times.Once);

あまり読みにくいですが、厳密なセットアップと厳密な検証の両方を使用する別の方法はないと思います。

17

最初の質問に答えるために、はい、2つのブロックは同等です。モックのメソッドが呼び出されなかった場合に_.Verify_が呼び出されると、どちらも失敗します。

私が知る限り、前もって検証を指定することはできません。それについて考えれば、それは理にかなっています。

これはモックの動作を指定しています:

_mock.Setup(m => m.ReturnSomething()).Returns(1);
_

これは、呼び出し元の動作を確認しています。

_mock.Verify(m => m.ReturnSomething(), Times.AtLeastOnce());
_

個人的には、呼び出し側の必要な動作を確認するために個別にverifyを呼び出すことを好みます。.Verifiable()および.Verify()は、それほど厳密ではないショートカットです(メソッドが1回以上呼び出されたことを確認するだけです)ただし、コードでメソッドを1回だけ呼び出す必要があることがわかっている場合は、最後に確認を入れて確認します。

コードのマージによってメソッドが2回呼び出された後、私はそれを始めました。少なくとも1回は呼び出されて以来、テストはパスしましたが、他の何かが発生してはならないはずのことも意味していました!

16
Trevor Pilley

Evren Kuzucuogluの答えを詳しく説明するために、次の拡張メソッドを作成して、式の作成を少し単純にしました。

/// <summary>
/// Creates a method call expression that can be passed to both <see cref="Setup"/> and <see cref="Verify"/>.
/// </summary>
/// <typeparam name="T">Mocked object type.</typeparam>
/// <param name="mock">Mock of <see cref="T"/>.</param>
/// <param name="expression">Method call expression to record.</param>
/// <returns>Method call expression.</returns>
public static Expression<Action<T>> CallTo<T>(this Mock<T> mock, Expression<Action<T>> expression) where T : class
{
    return expression;
}

/// <summary>
/// Creates a method call expression that can be passed to both <see cref="Setup"/> and <see cref="Verify"/>.
/// </summary>
/// <typeparam name="T">Mocked object type.</typeparam>
/// <typeparam name="TResult">Method call return type.</typeparam>
/// <param name="mock">Mock of <see cref="T"/>.</param>
/// <param name="expression">Method call expression to record.</param>
/// <returns>Method call expression.</returns>
public static Expression<Func<T, TResult>> CallTo<T, TResult>(this Mock<T> mock, Expression<Func<T, TResult>> expression) where T : class
{
    return expression;
}

使用例:

var createMapperCall = mockMappingFactory.CallTo(x => x.CreateMapper());
mockMappingFactory.Setup(createMapperCall).Returns(mockMapper.Object);

mockMappingFactory.Verify(createMapperCall, Times.Once());
5
Jack A.