当视图模型包含ICommand时,使用Mock进行单元测试

本文关键字:Mock 使用 单元测试 视图 模型 包含 ICommand | 更新日期: 2023-09-27 18:27:18

我有一个类(为了简单起见,我将其称为MyCustomCommand),它基本上接受Execute和CanExecute的两个委托。这个类实现ICommand。这样,我就可以在视图模型中声明一个从XAML绑定到的属性。

问题是,我遇到了一个场景,由于我的一个名为ApplyChangesCommand的命令中存在依赖项,我需要模拟视图模型。我还不相信依赖是件坏事。在这一点上,这是相对必要的。

由于这个依赖关系,我使用Mock Setups创建了一个回调,它基本上"不做任何事情"来规避依赖关系。

既然我已经模拟了视图模型,那么任何实例属性现在都是空的。这包括我的命令。

简单的例子是:

private void _somethingToExecute;
public ICommand ApplyChangesCommand { get { return MyCustomCommand(_somethingToExecute, e=>true); }

使用Mock有什么方法可以真正调用ApplyChangesCommand的_somethingToExecute吗?呼叫基地没有切断它,我想不出任何其他方法来做到这一点。

一种解决方法是将"_somethingToExecute"公开,并在我的测试中创建ApplyChangesCommand,但我不喜欢。

如有任何建议,不胜感激。

感谢

当视图模型包含ICommand时,使用Mock进行单元测试

好吧,现在我有更多的时间来研究这个问题,而不是在工作中仓促行事,我看到了问题。您应该使用一个注入的工厂来创建ApplyChangesCommand。然后,对于VM的单元测试,您只需验证该命令是否返回了工厂创建的命令。下面是一个例子:

public class MyViewModel
{
    private MyCustomCommandFactory _commandFactory;
    private void _somethingToExecute;
    public MyViewModel(MyCustomCommandFactory commandFactory)
    {
        _commandFactory = commandFactory;
    }
    public ICommand ApplyChangesCommand  
    { 
        get 
        { 
            return _commandFactory.Create(_somethingToExecute, e=>true);
        }
    }
}

这假设您希望在每次调用get时创建一个新命令(这似乎是设置它的方式)。如果您只想为VM的生命周期创建一个命令,那么显然您可以通过VM构造函数中的工厂来创建该命令。

要对此进行单元测试,您可以这样做:

[Test]
public void ApplyChangesCommand_Always_ReturnsFactoryCreatedCommand
{
    Mock<ICommand> mockCreatedCustomCommand = new Mock<ICommand>();
    Mock<MyCustomCommandFactory> mockCommandFactory = new Mock<MyCustomCommandFactory>();
    mockCreatedCustomCommand.Setup(p => p.Create(It.IsAny<Action>(), e => true))
                            .Returns(mockCreatedCustomCommand.Object);
    Assert.That(systemUnderTest.ApplyChangesCommand, Is.SameAs(mockCreatedCustomCommand.Object));
}

(将Action更改为代表的实际身份)。

这就是对VM命令进行单元测试所需要的全部内容。如果你想测试行为是否符合你的预期(也就是说,它执行传入的委托并使用该委托返回预期值),那么这就是验收测试的领域。

注意:我在测试示例中使用了Moq-mocking框架语法。

就我个人而言,我不会把这样的代表交给Command。相反,我会注入Command需要的任何东西,并在Command中拥有所有逻辑,在那里它更容易进行单元测试,并且对VM的依赖性更宽松。