使用 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 调用中提取表达式?

173523 次浏览

我一直在用同样的方式验证呼叫——我相信这是正确的方法。

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

如果你的lambda表达式变得笨拙,你可以创建一个函数,接受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的一个错误,其中错误消息指出方法在根本没有调用时被多次调用。他们现在可能已经修复了它——但如果您看到那条消息,您可能会考虑验证该方法是否实际被调用。

EDIT:这里有一个多次调用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时,它会返回特定于那个item的东西。或者,您可以使用一个接受itemId作为输入并返回内容的函数。

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

我以前在博客上看到的另一个方法(可能是Phil Haack ?)设置从某种脱队列对象返回-每次调用该函数时,它都会从队列中拉出一个项。

我认为问题在于Moq会检查是否相等。而且,由于XmlElement不重写Equals,它的实现将检查引用是否相等。

你不能使用自定义对象,这样你就可以重写等号了吗?

如果验证逻辑不是简单的,那么编写大型lambda方法就会很混乱(如示例所示)。您可以将所有测试语句放在一个单独的方法中,但我不喜欢这样做,因为这会打乱阅读测试代码的流程。

另一种方法是在Setup调用上使用回调来存储传递给模拟方法的值,然后编写标准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));

一个更简单的方法是:

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

也有一个,但是动作的参数是一个没有公共属性的接口。最后用一个单独的方法使用了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
}