【问题标题】:How to mock an interface that extends IEnumerable如何模拟扩展 IEnumerable 的接口
【发布时间】:2011-04-05 14:33:10
【问题描述】:

我正在使用 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 接口,然后它就可以工作了。

感谢任何帮助。

【问题讨论】:

    标签: c# mocking ienumerable moq


    【解决方案1】:

    如果 GetEnumerator() 被调用,您需要为它创建一个设置。比如:

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

    请注意确定这是否是本例中的问题,但值得注意的是您是否需要模拟 IEnumerable&lt;T&gt;

    【讨论】:

    • 让它更像 Moq:clonedGameBoardMock.Setup(g => g.GetEnumerator()).Returns(new Mock>().Object);
    【解决方案2】:

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

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

    这是一个完整的重现(使用 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<>...
        }
    }
    

    对于这个问题的价值/让这个问题突然出现在那个与我有同样问题的孤独的谷歌员工身上:上面是 Couchbase .NET 客户端的IView&lt;T&gt; 接口的抽象版本,它也实现了IEnumerable&lt;T&gt;

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        好的,如果有人感兴趣,我将 Moq 更新到第 4 版,现在一切正常。

        【讨论】:

          猜你喜欢
          • 2019-01-20
          • 1970-01-01
          • 2011-11-13
          • 1970-01-01
          • 2013-12-07
          • 1970-01-01
          • 1970-01-01
          • 2010-09-05
          • 1970-01-01
          相关资源
          最近更新 更多