Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 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?

like image 611
Micah Avatar asked Jun 08 '11 02:06

Micah


1 Answers

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; }
}
like image 133
Igor Zevaka Avatar answered Oct 19 '22 20:10

Igor Zevaka