Tony O'Hagan
Tony O'Hagan

Reputation: 22692

Moq: Test parameters sent with sequence of method calls

I'm new to C# Moq (used Rhino Mochs in the past) and needing to test a sequence of calls to the same method. I found this cool solution that tests a sequence of return values:

http://haacked.com/archive/2009/09/29/moq-sequences.aspx/

public static class MoqExtensions
{
  public static void ReturnsInOrder<T, TResult>(this ISetup<T, TResult> setup, 
    params TResult[] results) where T : class  {
    setup.Returns(new Queue<TResult>(results).Dequeue);
  }
}

What I need to do is to test the values sent as a parameter to the method (rather than the values it returns) in a sequence of calls to the same method.

Rough outline ...

 var expression = new MyExpressionThing();

 processor.Setup(x => x.Execute(expected1)).Verifiable();
 processor.Setup(x => x.Execute(expected2)).Verifiable();
 processor.Setup(x => x.Execute(expected3)).Verifiable();

 expression.ExecuteWith(processor.Object);
 processor.Verify();

Here's what I've attempted but I'm getting the exception:

"System.ArgumentException : Invalid callback. Setup on method with parameters (String,Object[]) cannot invoke callback with parameters (String)."

 // Arrange
 var processor = new Mock<IMigrationProcessor>();
 IList<string> calls = new List<string>();
 processor.Setup(p => p.Execute(It.IsAny<string>()))
    .Callback<string>(s => calls.Add(s));

// Act
var expr = new ExecuteScriptsInDirectoryExpression { SqlScriptDirectory = @"SQL2\1_Pre" };
expr.ExecuteWith(processor.Object);

// Assert
calls.ToArray().ShouldBe(new[]
   { "DELETE FROM PRE1A", "DELETE FROM PRE1B", "INSERT INTO PRE2\r\nLINE2" });

Looks like I'm using boilerplate code from the Moq "Getting Started" examples:

This link discusses this exception and links to the Moq code that fires it.

http://dailydevscoveries.blogspot.com.au/2011/04/invalid-callback-setup-on-method-with.html

Upvotes: 1

Views: 2630

Answers (1)

Joe White
Joe White

Reputation: 97676

I would use a callback to capture the parameter each time the mock is called, and then assert the result:

var parameters = new List<ParameterType>();
processor.Setup(x => x.Execute(It.IsAny<ParameterType>()))
    .Callback<ParameterType>(param => parameters.Add(param));

CallCodeUnderTest(processor.Object);

Assert.That(parameters, Is.EqualTo(new[] { expected1, expected2, expected3 }));

Update: Based on the error message you quoted, it looks like the method you're mocking takes a second parameter that's a params object[]. You don't need to specify that parameter when you call the method (which is why you don't need it in the Setup lambda), but you do need to specify it in the generic type parameters to .Callback. Change your Callback line to:

    .Callback<string, object[]>((s, o) => calls.Add(s));

Upvotes: 5

Related Questions