使用 Moq 模拟扩展 IList 的接口

Using Moq to mock an interface that extends IList

我正在尝试模拟一个扩展 IList 的接口并让模拟作为 IEnumerable 工作。我期待模拟对象包含一个可枚举的用户列表,而不是枚举产生任何结果。如果我更改接口 ITestEnumerable 扩展到 IEnumerable 而不是 IList,下面的代码将起作用。

public interface ITestEnumerable : IList<User>
{

}

[Fact]
public void TestTest()
{
    //Arrange
    var fakes = new List<User>()
    {
        new User() { DisplayName = "Joe Smith", Mail = "jsmith@test.com" },
        new User() { DisplayName = "Jane Doe", Mail = "jdoe@test.com" }
    };
    var mockTest = new Mock<ITestEnumerable>();
    mockTest.Setup(t => t.Count).Returns(() => fakes.Count());
    mockTest.Setup(t => t[It.IsAny<int>()]).Returns<int>(i => fakes.ElementAt(i));
    mockTest.As<IEnumerable<User>>().Setup(t => t.GetEnumerator()).Returns(() => fakes.GetEnumerator());
    var testList = new List<User>();

    //Act
    testList.AddRange(mockTest.Object);

    //Assert
    Assert.NotNull(testList[0]);
}

这里的问题出在 AddRange 函数的内部:https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,79de3e39e69a4811

您可以看到,第一次尝试是将其转换为 ICollection,然后是整个 CopyTo 流程。

所以最简单的就是用这样的东西代替它。无论如何,您都会模拟枚举器。它也可以用 LINQ 缩短。

foreach(var item in mockTest.Object)
{
    testList.Add(item);
}

如果你想用最小起订量来做,像这样的东西应该适合:

mockTest.As<ICollection<User>>().Setup(t => t.CopyTo(It.IsAny<User[]>(), It.IsAny<int>())).Callback<User[], int>((u,c) => fakes.CopyTo(u,c));