我有一个类,其中有一个 表达式主体成员:
public MyState
{
public List<Thing> Things { get; set; }
public double SomeValue => Things.sum(thing => thing.Value);
public double MyProperty { get; set; }
}
我需要为一个方法编写单元测试
Apply()
,其签名如下所示:
public OtherClass
{
public override MyList Apply(MyState state) {
state.MyProperty = state.SomeValue;
return null;
}
}
public void Test()
{
var mock = new Mock<MyState>();
mock.SetupGet(mock => mock.SomeValue).Returns(100.0);
var otherClass = new OtherClass();
otherClass.Apply(mock.Object);
...
}
这导致了一个错误,指出
SomeValue
是一个 不可重写的成员。
然后我实现了一个接口
IMyState
,如下所示:
public interface IMyState
{
double SomeValue { get; set; }
double MyProperty { get; set; }
}
并修改了我的测试以实现模拟的接口。这导致调用我想要测试的
Apply()
方法时出错,因为 Apply
采用 MyState
的实例而不是 IMyState
:
public void Test()
{
var mock = new Mock<MyState>().As<IMyState>(); // implement interface here
mock.SetupGet(mock => mock.SomeValue).Returns(100.0);
var otherClass = new OtherClass();
otherClass.Apply(mock.Object); // mock.Object is of class IMyState
...
}
我猜这是因为
MyState
实际上并没有实现IMyState
。 是否有某种方法可以模拟此属性以返回指定值,而无需更改 MyState
来实现 IMyState
(或将 SomeValue
声明为 virtual
,我在其他 StackOverflow 帖子中看到过)? I' d 不希望仅出于测试目的而更改现有代码。
我尝试将模拟转换回
MyState
,然后将其传递给Apply()
,但是当我将模拟设置为返回时,SomeValue
随后返回0
而不是100.0
:
public void Test()
{
var mock = new Mock<MyState>().As<IMyState>();
mock.SetupGet(mock => mock.SomeValue).Returns(100.0);
var otherClass = new OtherClass();
otherClass.Apply((MyState)mock.Object); // after casting, mock.Object.SomeValue returns 0
...
}
如果你正在尝试测试
Apply
方法,并且你依赖于你想要模拟的 MyState
- 并且因为你不想更改它 - 答案很简单 - 你不能。
仅限接口或虚拟成员。故事结束。
但我猜问题在于
List<Thing>
的创建。我会使用 AutoFixture nuget 包来实现这一点。那么事情就变得非常简单了:
var fixture = new Fixture();
var things = fixture.Build<Thing>()
.With(x => x.Value, 3) // for each created Thing set the Value to 3
.CreateMany(3) // create 3 things
.ToList();
var state = fixture.Create<MyState>();
state.Things = things;
// Now you know that state.SomeValue should equal to 9.
// To further simplify, you can always sat Value on
// Thing to 0 in the test, then you know state.SomeValue
// would be always 0, regardless of number of items.