最近我在一个C#项目中发现了Moq库(4.5.21)非常有趣的行为.以下是我要测试的课程.
public class Order { public string State { get; set; } } public interface IOrderService { Task UpdateOrderAsync(Order order); } public class Program { public async Task RunAsync(IOrderService orderService) { var order = new Order(); order.State = "new"; await orderService.UpdateOrderAsync(order); order.State = "open"; await orderService.UpdateOrderAsync(order); } }
下面是我的TestClass:
[TestMethod] public async Task TestMethod() { var mock = new Mock<IOrderService>(); await new Program().RunAsync(mock.Object); mock.Verify(x => x.UpdateOrderAsync(It.Is<Order>(o => o.State == "new")),Times.Once); mock.Verify(x => x.UpdateOrderAsync(It.Is<Order>(o => o.State == "open")),Times.Once); }
我得到以下输出:
Moq.MockException: Expected invocation on the mock once,but was 0 times: x => x.UpdateOrderAsync(It.Is<Order>(o => o.State == "new")) Configured setups: x => x.UpdateOrderAsync(It.Is<Order>(o => o.State == "new")),Times.Once x => x.UpdateOrderAsync(It.Is<Order>(o => o.State == "open")),Times.Once Performed invocations: IOrderService.UpdateOrderAsync(Order<State:open>) IOrderService.UpdateOrderAsync(Order<State:open>)
我希望我可以验证方法是否每次调用一次具有不同状态的对象.关于我做错了什么的任何想法?
谢谢!
解决方法
这与
this post非常相似
基本上是因为在调用第一个验证时,模拟持有的引用(对于特定调用)已将其State属性更改为“open”(就在第二次调用之前).这可以通过将Order类更改为struct来证明,以确保mock捕获对象的副本而不是对对象本身的引用.
编辑:
为了得到完整的答案,上面的帖子使用的Expect现在已经过时了,这是一个通过测试:
var mock = new Mock<IOrderService>(MockBehavior.Loose); var newCalled = false; var openCalledBeforeNew = false; mock.Setup(x => x.UpdateOrderAsync(It.Is<Order>(o => o.State == "new"))).Callback(() => newCalled = !openCalledBeforeNew).Returns(Task.FromResult((object)null)); mock.Setup(x => x.UpdateOrderAsync(It.Is<Order>(o => o.State == "open"))).Callback(() => openCalledBeforeNew = newCalled).Returns(Task.FromResult((object)null)); await new Program().RunAsync(mock.Object); Assert.IsTrue(newCalled); Assert.IsTrue(openCalledBeforeNew);