Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking GetEnumerator using Moq

Tags:

c#

moq

I'm trying to mock the Variables interface in Microsoft.Office.Interop.Word assembly

var variables = new Mock<Variables>();
variables.Setup(x => x.Count).Returns(2);
variables.Setup(x => x.GetEnumerator()).Returns(TagCollection);

private IEnumerator TagCollection()
{
    var tag1 = new Mock<Variable>();
    tag1.Setup(x => x.Name).Returns("Foo");
    tag1.Setup(x => x.Value).Returns("Bar");

    var tag2 = new Mock<Variable>();
    tag2.Setup(x => x.Name).Returns("Baz");
    tag2.Setup(x => x.Value).Returns("Qux");

    yield return tag1.Object;
    yield return tag2.Object;
}

I have code that reads like the following:

// _variables is an instance of Variables interface
var tags = from variable in _variables.OfType<Variable>()
           where variable.Name == "Foo"
           select variable.Value;
var result = tags.ToList();

Last line in the code above throws a NullReferenceException. If I use a foreach loop to iterate through _variables collection, I could access mock objects of Variable without any problem. What am I doing wrong here?

like image 413
Raj Avatar asked Mar 06 '15 19:03

Raj


1 Answers

Try:

variables
    .As<IEnumerable>()
    .Setup(x => x.GetEnumerator()).Returns(TagCollection);

There are two different methods, one declared in the base interface and one declared in Variables.

When you foreach directly, the latter is called because that method hides the same-looking member from the base type. foreach calls the public method when one exists, and in that case IEnumerable does not matter.

When you call the .OfType<Variable>() Linq extension, the reference is cast to IEnumerable interface, and the name hiding is no longer present. The method on the base interface is called.

It is like the difference between:

_variables.GetEnumerator();

and:

((IEnumerable)_variables).GetEnumerator();

You can think of the mock Moq generates as this:

public class TheTypeMoqMakes : Variables 
{
  Enumerator Variables.GetEnumerator()
  {
    // Use return value from after
    // expression tree you provided with 'Setup' without 'As'.
    // If you did not provide one, just return null.
  }

  Enumerator IEnumerable.GetEnumerator()
  {
    // Use return value from after
    // expression tree you provided with 'Setup' with 'As<IEnumerable>'.
    // If you did not provide one, just return null.
  }

  // other methods and properties
}

The reason why Moq returns null in case the member was not Setup, is that you have MockBehavior.Loose. Always consider MockBehavior.Strict instead.


I cannot understand why the authors of Variables interface chose to use method hiding in this case.

like image 121
Jeppe Stig Nielsen Avatar answered Sep 21 '22 03:09

Jeppe Stig Nielsen