如何模拟没有 getter 的表达式主体成员

问题描述 投票:0回答:1

背景

我有一个类,其中有一个 表达式主体成员

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

    ...
}
c# unit-testing moq expression-bodied-members
1个回答
2
投票

如果你正在尝试测试

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.
© www.soinside.com 2019 - 2024. All rights reserved.