开发者

How to verify multiple method calls with Moq

So the scenario is this: a user does some action (like earn a badge or unlock something) and an email notification gets sent out. One to the user (with a message like "You've unlocked XYZ...") and then a different message开发者_如何学C to each of their friends like ("You're friend has unlocked XYZ...").

public interface INotify
{
   void Notify(User user, User friend);
}

public class NotificationService
{
    private IEnumerable<INotify> _notifiers;

    public NotificationService(IEnumerable<INotify> notifiers)
    {
        _notifiers = notifiers;
    }

    public SendNotifications()
    {
        User user = GetUser();
        IEnumerable<User> friends = GetFriends();

        foreach(var notifier in _notifiers)
        {
            //Send notification to user
            notifier.Notify(user, null);

            //send notification to users friends
            foreach(var friend in friends)
                notifier.Notify(user, friend);
        }
    }
}

I'm trying to use moq to test that each notifier is called 2x. Once passing null as the second parameter and the second time passing in a value to both parameters.

[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());
}

The problem is that the second verify call throws an ArgumentNullException for the second parameter. Is there away to say "Check the first call has these parameters, and then the second call has other parameters". I know I can get it around it simply by calling:

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

But I was wanting to be a little more specific. Anyway to do this?


You can achieve this by recording what happens on each call to Notify. Then you can compare the recording to what's expected:

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

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

    var invocations = new List<NotifyParams>();

    notifierMock
        .Setup(f => f.Notify(It.IsAny<User>(), It.IsAny<User>()))
        .Callback<string, string>((user, friend) => invocations.Add(new NotifyParams{user = user, friend = friend}));

    Assert.AreEqual(1, invocations[0].user.UserId);
    Assert.IsNull(invocations[0].friend);
    Assert.AreEqual(1, invocations[1].user.UserId);
    Assert.AreEqual(2, invocations[1].user.UserId);
}

public struct NotifyParams { 
    public User user {get;set;}
    public User friend { get; set; }
}


You can create a method for get User list. Then get user by It.Is method.

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);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜