web-dev-qa-db-ja.com

Moqを使用して特定のパラメーターを検証する

public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
    var messageServiceClientMock = new Mock<IMessageServiceClient>();
    var queueableMessage = CreateSingleQueueableMessage();
    var message = queueableMessage[0];
    var xml = QueueableMessageAsXml(queueableMessage);
    messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(xml)).Verifiable();
    //messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();

    var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
    serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(essageServiceClientMock.Object);
    var loggerStub = new Mock<ILogger>();

    var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
    client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});

    //messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(xml), Times.Once());
    messageServiceClientMock.Verify();
}

Moqを使い始めて、少し苦労しています。 messageServiceClientが正しいパラメーター(XmlElement)を受け取っていることを確認しようとしていますが、機能させる方法が見つかりません。特定の値をチェックしない場合にのみ機能します。

何か案は?

部分的な回答:プロキシに送信されたxmlが正しいことをテストする方法を見つけましたが、それを行う正しい方法だとはまだ思いません。

public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
    var messageServiceClientMock = new Mock<IMessageServiceClient>();
    messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();
    var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
    serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(messageServiceClientMock.Object);
    var loggerStub = new Mock<ILogger>();

    var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
    var message = CreateMessage();
    client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});

    messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(It.Is<XmlElement>(xmlElement => XMLDeserializer<QueueableMessage>.Deserialize(xmlElement).Messages.Contains(message))), Times.Once());
}

ところで、Verify呼び出しから式を抽出するにはどうすればよいですか?

140
Luis Mirabal

検証ロジックが自明ではない場合、大きなラムダメソッドを書くのは面倒です(例が示すように)。すべてのテストステートメントを別のメソッドに入れることもできますが、テストコードの読み取りの流れを妨げるため、これを行うのは好きではありません。

もう1つのオプションは、セットアップコールでコールバックを使用して、モックメソッドに渡された値を保存し、それを検証する標準のAssertメソッドを記述することです。例えば:

// Arrange
MyObject saveObject;
mock.Setup(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()))
        .Callback<int, MyObject>((i, obj) => saveObject = obj)
        .Returns("xyzzy");

// Act
// ...

// Assert
// Verify Method was called once only
mock.Verify(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()), Times.Once());
// Assert about saveObject
Assert.That(saveObject.TheProperty, Is.EqualTo(2));
210
Rich Tebb

私は同じ方法で通話を確認してきました-それが正しい方法だと思います。

mockSomething.Verify(ms => ms.Method(
    It.IsAny<int>(), 
    It.Is<MyObject>(mo => mo.Id == 5 && mo.description == "test")
  ), Times.Once());

ラムダ式が扱いにくい場合、入力としてMyObjectを取り、true/false...を出力する関数を作成できます。

mockSomething.Verify(ms => ms.Method(
    It.IsAny<int>(), 
    It.Is<MyObject>(mo => MyObjectFunc(mo))
  ), Times.Once());

private bool MyObjectFunc(MyObject myObject)
{
  return myObject.Id == 5 && myObject.description == "test";
}

また、メソッドがまったく呼び出されなかったときにメソッドが複数回呼び出されたことをエラーメッセージに示すMockのバグにも注意してください。今では修正されているかもしれませんが、そのメッセージが表示された場合は、メソッドが実際に呼び出されたことを確認することを検討してください。

編集:以下は、リスト内の各オブジェクトの関数を呼び出すことを確認したいシナリオで、複数回verifyを呼び出す例です(たとえば)。

foreach (var item in myList)
  mockRepository.Verify(mr => mr.Update(
    It.Is<MyObject>(i => i.Id == item.Id && i.LastUpdated == item.LastUpdated),
    Times.Once());

セットアップの同じアプローチ...

foreach (var item in myList) {
  var stuff = ... // some result specific to the item
  this.mockRepository
    .Setup(mr => mr.GetStuff(item.itemId))
    .Returns(stuff);
}

そのため、そのitemIdに対してGetStuffが呼び出されるたびに、そのアイテムに固有のものが返されます。または、itemIdを入力として受け取り、ものを返す関数を使用できます。

this.mockRepository
    .Setup(mr => mr.GetStuff(It.IsAny<int>()))
    .Returns((int id) => SomeFunctionThatReturnsStuff(id));

ブログで見たもう1つの方法(Phil Haackかもしれませんか?)には、ある種のデキューオブジェクトから戻る設定がありました。関数が呼び出されるたびに、キューからアイテムを取得していました。

83
Mayo

より簡単な方法は次のとおりです。

ObjectA.Verify(
    a => a.Execute(
        It.Is<Params>(p => p.Id == 7)
    )
);
15
dmitry.sergeyev

Moqが平等をチェックするという事実に問題があると思います。また、XmlElementはEqualsをオーバーライドしないため、その実装は参照の等価性をチェックします。

カスタムオブジェクトを使用できないので、equalsをオーバーライドできますか?

1
Fernando

これらのいずれかもありましたが、アクションのパラメーターはパブリックプロパティのないインターフェイスでした。別のメソッドでIt.Is()を使用することになり、このメソッド内でインターフェースのモックをしなければなりませんでした

public interface IQuery
{
    IQuery SetSomeFields(string info);
}

void DoSomeQuerying(Action<IQuery> queryThing);

mockedObject.Setup(m => m.DoSomeQuerying(It.Is<Action<IQuery>>(q => MyCheckingMethod(q)));

private bool MyCheckingMethod(Action<IQuery> queryAction)
{
    var mockQuery = new Mock<IQuery>();
    mockQuery.Setup(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition())
    queryAction.Invoke(mockQuery.Object);
    mockQuery.Verify(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition(), Times.Once)
    return true
}
1
ds4940