Zoliqa
Zoliqa

Reputation: 1025

How to mock an interface that extends IEnumerable

I'm using Moq and I have the following interface:

public interface IGameBoard : IEnumerable<PieceType>
{
    ...  
}
public class GameBoardNodeFactory
{
    public virtual GameBoardNode Create (int row, int column, IGameBoard gameBoard)
    {
        ...
    }
}

Then I have a test like this:

var clonedGameBoardMock = new Mock<IGameBoard> (MockBehavior.Loose);
var gameBoardNodeFactoryMock = new Mock<GameBoardNodeFactory> ();
gameBoardNodeFactoryMock.Setup (x =>
    x.Create (
        position.Row,
        position.Column,
        clonedGameBoardMock.Object)).Returns (new GameBoardNode { Row = position.Row, Column = position.Column });

But then gameBoardNodeFactoryMock.Object.Create (position.Row, position.Column, clonedGameBoardMock.Object) throws a NullReferenceException. I tried to create a mock for the IGameBoard such that it doesn't extend IEnumerable<PieceType> interface and then it works.

Any help is appreciated.

Upvotes: 10

Views: 6235

Answers (4)

Jeroen
Jeroen

Reputation: 63830

The answer by @DanBryant was also the key to our solution. However, the enumerator in that case might be accidentally reused. Instead, I suggest using:

clonedGameBoardMock.Setup(g => g.GetEnumerator()).Returns(() => mockPieces.GetEnumerator());

Here's a full repro (new class library using NUnit 2.6.4 and Moq 4.2):

public interface IMyThing<T> : IEnumerable<T>
{
    string Name { get; set; }

    IMyThing<T> GetSub<U>(U key);
}

public interface IGenericThing
{
    string Value { get; set; }
}

public class Pet
{
    public string AnimalName { get; set; }
}

public class Unit
{
    public IEnumerable<Pet> ConvertInput(IMyThing<IGenericThing> input)
    {
        return input.GetSub("api-key-123").Select(x => new Pet { AnimalName = x.Value });
    }
}

[TestFixture]
public class Class1
{
    [Test]
    public void Test1()
    {
        var unit = new Unit();
        Mock<IMyThing<IGenericThing>> mock = new Mock<IMyThing<IGenericThing>>();
        Mock<IMyThing<IGenericThing>> submock = new Mock<IMyThing<IGenericThing>>();

        var things = new List<IGenericThing>(new[] { new Mock<IGenericThing>().Object });

        submock.Setup(g => g.GetEnumerator()).Returns(() => things.GetEnumerator());
        mock.Setup(x => x.GetSub(It.IsAny<string>())).Returns(submock.Object);

        var result = unit.ConvertInput(mock.Object);
        Assert.That(result, Is.Not.Null.And.Not.Empty);
        Assert.That(result, Is.Not.Null.And.Not.Empty); // This would crash if the enumerator wasn't returned through a Func<>...
    }
}

For what it's worth / to make this question pop up to that one lone Googler with the same problem I had: the above is an abstracted version of the Couchbase .NET client's IView<T> interface, which also implements IEnumerable<T>.

Upvotes: 4

Zoliqa
Zoliqa

Reputation: 1025

Okay if anyone is interested, I updated Moq to version 4 and now everything works as expected.

Upvotes: 0

Dan Bryant
Dan Bryant

Reputation: 27515

You would need to create a Setup for GetEnumerator() if it's being called. Something like:

var mockPieces = new List<PieceType>;
clonedGameBoardMock.Setup(g => g.GetEnumerator()).Returns(mockPieces.GetEnumerator());

Note sure if that's the issue in this case, but worth noting if you ever need to mock IEnumerable<T>.

Upvotes: 13

Jon
Jon

Reputation: 363

A null reference in this situation usually means your setup was never met. Meaning it was never called with the exact values you set it up for. To debug this I would make your match less constraining by using It.IsAny() and so on to make sure the test will match on any call to the mocked function. In most cases this is good enough. Any reason why your are trying to match on specific values?

Upvotes: 0

Related Questions