C# 如何使用Moq验证多个方法调用

C# 如何使用Moq验证多个方法调用,c#,testing,.net-4.0,mocking,moq,C#,Testing,.net 4.0,Mocking,Moq,因此,场景是这样的:用户执行一些操作(比如赢得徽章或解锁某些东西),然后发送电子邮件通知。一封给用户(带有类似“You's unlocked XYZ…”的消息),然后一封不同的消息给他们的每个朋友,如(“You's friend has unlocked XYZ…”) 公共接口INotify { 作废通知(用户、用户朋友); } 公共类通知服务 { 私有IEnumerable通知程序; 公共通知服务(IEnumerable通知程序) { _通知者=通知者; } 公共发送通知() { User=G

因此,场景是这样的:用户执行一些操作(比如赢得徽章或解锁某些东西),然后发送电子邮件通知。一封给用户(带有类似“You's unlocked XYZ…”的消息),然后一封不同的消息给他们的每个朋友,如(“You's friend has unlocked XYZ…”)

公共接口INotify
{
作废通知(用户、用户朋友);
}
公共类通知服务
{
私有IEnumerable通知程序;
公共通知服务(IEnumerable通知程序)
{
_通知者=通知者;
}
公共发送通知()
{
User=GetUser();
IEnumerable friends=GetFriends();
foreach(变量通知程序在_通知程序中)
{
//向用户发送通知
notifier.Notify(用户,空);
//向用户朋友发送通知
foreach(varfriends in friends)
通知者。通知者(用户、朋友);
}
}
}
我试图使用moq来测试每个通知程序是否被称为2x。一次将null作为第二个参数传递,第二次将值传递给这两个参数

[Test]
public void MakeSureEveryoneIsNotified()
{
     var notifierMock = new Mock<INotifier>();

     var svc = new NotificationService(new List<INotifier>{ notifierMock.Object });    
     svc.SendNotifications();

     notifierMock.Verify(x => x.Notify(It.Is<User>(user => user.UserId == 1), null), Times.Once());
     notifierMock.Verify(x => x.Notify(It.Is<User>(user => user.UserId == 1), It.Is<User>(user => user.UserId == 2)), Times.Once());
}
[测试]
public void MakeSureEveryoneIsNotified()
{
var notifierMock=new Mock();
var svc=newnotificationservice(新列表{notifierMock.Object});
svc.SendNotifications();
Verify(x=>x.Notify(It.Is(user=>user.UserId==1),null),Times.Once();
Verify(x=>x.Notify(It.Is(user=>user.UserId==1),It.Is(user=>user.UserId==2)),Times.Once();
}
问题是第二个verify调用为第二个参数抛出ArgumentNullException。是否可以说“检查第一次呼叫是否有这些参数,然后第二次呼叫是否有其他参数”。我知道我可以通过打电话来解决问题:

notifierMock.Verify(x => x.Notify(It.IsAny<User>(), It.IsAny<User>()), Times.Exactly(2));
notifierMock.Verify(x=>x.Notify(It.IsAny(),It.IsAny()),Times.justice(2));

但我想说得更具体一点。无论如何要这样做?

您可以通过记录每次调用
Notify
时发生的情况来实现这一点。然后,您可以将录制内容与预期内容进行比较:

[TestMethod]
公共假期
{
var notifierMock=new Mock();
var svc=newnotificationservice(新列表{notifierMock.Object});
svc.SendNotifications();
var调用=新列表();
布告室
.Setup(f=>f.Notify(It.IsAny(),It.IsAny())
.Callback((user,friend)=>invocations.Add(newnotifyparams{user=user,friend=friend}));
Assert.AreEqual(1,调用[0].user.UserId);
Assert.IsNull(调用[0].friend);
Assert.AreEqual(1,调用[1].user.UserId);
Assert.AreEqual(2,调用[1].user.UserId);
}
公共结构NotifyParams{
公共用户{get;set;}
公共用户好友{get;set;}
}

您可以创建获取用户列表的方法。然后通过它获取用户。Is方法

private bool GetUser(User user, List<User> users)
{
    if (user != null)
        users.Add(user);

    return true;
}
private bool GetUser(用户用户,列表用户)
{
如果(用户!=null)
用户。添加(用户);
返回true;
}
[测试]
public void MakeSureEveryoneIsNotified()
{
var notifierMock=new Mock();
var svc=newnotificationservice(新列表{notifierMock.Object});
svc.SendNotifications();
var users=新列表();
var friends=新列表();
//验证调用该方法的次数
布告室
.验证(x=>x.通知(
It.Is(u=>GetUser(u,users)),
It.Is(f=>GetFriend(f,friends))
)正是(2));
Assert.arequals(2,users.Count);
Assert.arequals(1,用户[0].UserId);
Assert.arequals(1,用户[1].UserId);
Assert.AreEquals(1,friends.Count);
Assert.arequals(2,朋友[0].UserId);
}

您是否尝试在.Verify语句之外创建一个用户并将该用户用作参数?我将删除对GetUser()方法的依赖,创建一个用户提供程序,模拟它,然后存根提供程序的相关部分。GetUser方法实际上来自注入类中的外部依赖项,但为了简洁起见,我把它省略了。不过,第一种选择应该是可行的
private bool GetUser(User user, List<User> users)
{
    if (user != null)
        users.Add(user);

    return true;
}
[Test]
public void MakeSureEveryoneIsNotified()
{
     var notifierMock = new Mock<INotifier>();

     var svc = new NotificationService(new List<INotifier>{ notifierMock.Object });    
     svc.SendNotifications();

     var users = new List<User>();
     var friends = new List<User>();

     // verify how many times call the method
     notifierMock
        .Verify(x => x.Notify(
            It.Is<User>(u => GetUser(u, users)), 
            It.Is<User>(f => GetFriend(f, friends))
        ), Times.Exactly(2));

    Assert.AreEquals(2, users.Count);
    Assert.AreEquals(1, users[0].UserId);
    Assert.AreEquals(1, users[1].UserId);

    Assert.AreEquals(1, friends.Count);
    Assert.AreEquals(2, friends[0].UserId);
}