Reputation: 2197
I am writing C# unit tests uning Moq and am seeing what I consider to be very strange behaviour with the Moq library.Verify behaviour.
If I run the code below with the Linq.Select line enabled and the hand-rolled iteration commented out I get:
Expected invocation on the mock exactly 9 times, but was 18 times: e => e.ExampleMethod(It.IsAny()) Total tests: 1. Passed: 0. Failed: 1. Skipped: 0.
If I run the test with the Linq.Select commented out and the hand rolled iteration enabled, I get:
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
If I were to enable the Linq.Select statement and copy paste even more Console.WriteLine calls, then the verified call count increases by 9 for each line of output; which seems truly bizarre to me.
[Test]
public void TestPossibleMoqBug() {
var exampleMock = new Mock<IExample>();
exampleMock.Setup(e => e.ExampleMethod(It.IsAny<string>()))
.Callback<string>(m => { Console.WriteLine($"ExampleMethod called with '{m}'."); });
var examples = new[] { "a", "b", "c" };
var output = new Dictionary<string, IEnumerable<int>> {};
foreach (var example in examples) {
// Results in 18 calls (with two Console.WriteLines below)
output[example] = examples.Select(exampleMock.Object.ExampleMethod);
// Results in 9 calls (with two Console.WriteLines below)
//var results = new List<int>();
//foreach (var innerExample in examples) {
// results.Add(exampleMock.Object.ExampleMethod(innerExample));
//}
//output[example] = results;
}
// For each line of output here it results in another 'verified' call when using Linq.Select
Console.WriteLine("Flat view: " + output.Values.SelectMany(x => x).Count());
Console.WriteLine("Flat view: " + output.Values.SelectMany(x => x).Count());
exampleMock.Verify(e => e.ExampleMethod(It.IsAny<string>()), Times.Exactly(examples.Length * examples.Length));
}
public interface IExample {
int ExampleMethod(string message);
}
Upvotes: 1
Views: 782
Reputation: 921
Look at the contents of output in the debugger. The values are query objects, not lists as in your commented code. So at the first call to print Flat view, Example method has been called no times. It is called nine times when printing. It is then called another nine times on the second print call. If you want the test to pass with the current assert, enumerate the query when building the dictionary:
output[example] = examples.Select(exampleMock.Object.ExampleMethod).ToList();
Upvotes: 2