67

I am developing tests for an application. There's a method that has a params array as a parameter. I have set up the method using Moq but when I run the test, the return value of the method is null, which means it is not being mocked.

Here's a code sample:

public interface ITicketManager {
    string GetFirstTicketInQueueIfMatches(params string[] ticketsToMatch);
}

public class TicketManager : ITicketManager {
    private Queue<string> ticketQueue = new Queue<string>();

    public string GetFirstTicketInQueueIfMatches(params string[] ticketsToMatch) {
        var firstQueuedTicket = ticketQueue.Peek();
        var firstQueuedTicketMatchesAnyOfRequested = ticketsToMatch.Any(t => t == firstQueuedTicket);

        if(firstQueuedTicketMatchesAnyOfRequested)
            return firstQueuedTicket;

        return null;
    }
}

The mock code looks like this:

var mock = new Mock<ITicketManager>();

mock.Setup(m => m.GetFirstTicketInQueueIfMatches(It.IsAny<string>()))
    .Returns(p => { 
    if(p.Contains("A"))
            return "A";

    return null;
});

Why is it never hitting the mocked method?

ruffin
  • 16,507
  • 9
  • 88
  • 138
Luis Aguilar
  • 4,331
  • 6
  • 36
  • 55

3 Answers3

107

You're trying to call a method taking a single string, rather than an array. Bear in mind that it's the C# compiler which handles the params part, converting calling code which just specifies individual values into a call passing in an array. As far as the method itself is concerned, it's just getting an array - and that's what you're mocking.

The compiler is actually turning your code into:

mock.Setup(m => m.GetFirstTicketInQueueIfMatches
                        (new string[] { It.IsAny<string>() }))

which isn't what you want.

You should use:

mock.Setup(m => m.GetFirstTicketInQueueIfMatches(It.IsAny<string[]>()))

If you need to verify that it only gets given a single value, you'll need to do that in the same way you would for a non-params parameter.

Basically, params only makes a difference to the C# compiler - not to moq.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    @DiegoAC: Well you've got it *conditionally* returning `null` anyway. Have you tried making it return "Foo" instead if `p` doesn't contain `"A"`? That way you'd at least see it being invoked... – Jon Skeet Nov 01 '11 at 14:42
  • Excellent answer Jon. Thanks. Actually I was receiving only a single string. The explanation about the compiler was very useful. – Luis Aguilar Nov 01 '11 at 14:49
19

I believe the params string has to be matched by It.IsAny<string[]>() rather than It.IsAny<string>()

Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
2

Using Moq, the code below works to setup a callback on a method with a params argument. Defining the second argument as an array does the trick.

        MockLogger
            .Setup(x => x.Info(It.IsAny<string>(), It.IsAny<object[]>()))
            .Callback<string, object[]>((x, y) => _length = x.Length);
Ben Wesson
  • 589
  • 6
  • 16