使用 Moq 模拟 Fluent 界面

Mocking a Fluent interface using Moq

我在这里查看了很多关于这个主题的问题,但 none 似乎解决了我遇到的问题。

我的代码看起来有点像这样...

IBaseDataCollector<MyClass> myDataCollector;

myDataCollector = new Mock<IBaseDataCollector<MyClass>>();

systemUnderTest = new Thing(myDataCollector.Object);

而在我的 Thing class...

var collection = myDataCollector.SomeMethod()
                     .SomeSecondMethod()
                     .GetData();

其中 SomeMethod()SomeSecondMethod() return this(即 myDataCollector 的实例)

当我 运行 我的测试我得到一个 NullReferenceException 我调用 myDataCollector.

我尝试在我的测试设置中添加这个...

myDataCollector.Setup(_=> _.SomeMethod()),Returns(myDataCollector.Object);

但这甚至无法编译,抱怨它 "Could not resolve method 'Returns(IBaseDataCollector)'"

现在,如果我重构 Thing class 以阅读...

myDataCollector.SomeMethod();
myDataCollector.SomeSecondMethod()
var collection = myDataCollector.GetData();

我的测试正确执行。

如果是这样,我会重构我的代码并继续生活,但实际上,我需要在 SelectMany 调用中调用我的代码...

var collection = list.SelectMany(_=> myDataCollector.SomeMethod()
                     .SomeSecondMethod(_)
                     .GetData());

同样,我知道我 可以 SelectMany 替换为 ForEach 并使用每次迭代的结果手动填充集合对 GetData() 的调用,这样我就可以摆脱调用的流畅元素,但这意味着重构代码只是为了让测试工作,这感觉不对。

我应该如何在我的 Mocked 对象上调用 Setup() 才能使我的流畅调用正常工作?

看看下面的测试代码(我发明了一些细节来填补空白)。如图所示,模拟对象实例应该可以从其自己的方法中作为 return 的值使用。

    public class UnitTestExample
    {

        [Fact]
        public void UnitTestExample1()
        {
            var myClassInterfaceMock = new Mock<IInterface<MyClass>>();
            var instance = myClassInterfaceMock.Object;
            var myList = new List<MyClass>()
            {
                new MyClass() { Attribute = 1 }
            };

            myClassInterfaceMock.Setup(_ => _.SomeMethod()).Returns(instance);
            myClassInterfaceMock.Setup(_ => _.SomeSecondMethod()).Returns(instance);
            myClassInterfaceMock.Setup(_ => _.GetData()).Returns(myList);

            var myDependentClass = new MyDependentClass(instance);
            var result = myDependentClass.DoTheThing();

            Assert.True(result.Count.Equals(1));
        }
    }

    public interface IInterface<T>
    {
        IInterface<T> SomeMethod();
        IInterface<T> SomeSecondMethod();
        List<T> GetData();
    }

    public class MyClass
    {
        public int Attribute { get; set; }
    }

    public class MyDependentClass
    {
        private readonly IInterface<MyClass> _test;

        public MyDependentClass(IInterface<MyClass> test)
        {
            _test = test;
        }

        public List<MyClass> DoTheThing()
        {
            return _test.SomeMethod().SomeSecondMethod().GetData();
        }
    }