如何模拟扩展IEnumerable的接口

时间:2011-04-05 14:33:10

标签: c# mocking ienumerable moq

我正在使用Moq,我有以下界面:

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

然后我有这样的测试:

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 });

然后gameBoardNodeFactoryMock.Object.Create(position.Row,position.Column,clonedGameBoardMock.Object)抛出NullReferenceException。我试图为IGameBoard创建一个模拟,这样它就不会扩展IEnumerable&lt; PieceType&gt;接口,然后它工作。

感谢任何帮助。

4 个答案:

答案 0 :(得分:12)

如果要调用GetEnumerator(),则需要为其创建一个Setup。类似的东西:

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

请注意在这种情况下是否存在问题,但值得注意的是,您是否需要模拟IEnumerable<T>

答案 1 :(得分:4)

The answer by @DanBryant也是我们解决方案的关键。但是,在这种情况下,调查员可能会被意外重用。相反,我建议使用:

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

这是一个完整的repro(使用NUnit 2.6.4和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<>...
    }
}

为了这个问题/使这个问题突然出现在一个单独的Google员工身上,我遇到了同样的问题:上面是Couchbase .NET客户端的IView<T>接口的抽象版本,它也实现了{{ 1}}。

答案 2 :(得分:0)

在这种情况下,空引用通常意味着您的设置从未得到满足。这意味着它从未使用您设置的确切值调用。为了调试这个,我会通过使用It.IsAny()等来减少约束,以确保测试在对mocked函数的任何调用时都匹配。在大多数情况下,这已经足够了。您尝试匹配特定值的任何原因?

答案 3 :(得分:0)

好的,如果有人有兴趣,我将Moq更新到版本4,现在一切都按预期工作。