使用 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();
}
}
我在这里查看了很多关于这个主题的问题,但 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();
}
}