【问题标题】:Unit Testing ICommand and Methods单元测试命令和方法
【发布时间】:2020-05-26 12:52:34
【问题描述】:

我已经开发了一个使用 MVVM 作为架构的 WPF 应用程序,并且正在编写一些单元测试作为其中的一部分。在我看来,我有一个绑定到 ViewModel 中的属性的按钮,如下所示。

视图模型

public ICommand MoreInfoCommand
{
    get
    {
        if (_moreInfoCommand == null) 
        {
             _moreInfoCommand = new RelayCommand(parameter => OpenLink());
        }
        return _moreInfoCommand;
    }
}

private void OpenLink() 
{
    try 
    {
         Process.Start("https://www.google.com/");
    }
    catch
    {
      // catch error...
    }
}

在我的单元测试中,我创建了以下单元测试:

单元测试

[Description("Test MoreInfoCommand")]
[TestMethod]
public void TestMoreInfoCommand() 
{
     viewModel vm = new viewModel();
     Assert.IsTrue(vm.MoreInfoCommand.CanExecute(null));
}

目前这会测试该属性以查看在 UI 中单击按钮时是否可以执行相关方法。这会在条件得到满足时通过,但是我的理解是我还需要测试功能。考虑到这一点,例如,我如何测试单击按钮时是否发生了正确的功能。即,如何测试由于在 UI 中单击按钮而执行 ICommand MoreInfoCommand 时发生的情况。

我知道可以直接测试私有方法,但是在我的单元测试中我应该检查功能,如果是,我该怎么做。

提前致谢。

【问题讨论】:

  • 在您的情况下,您的命令会生成一个进程,因此您的单元测试可以检查该进程是否正在运行?
  • 我如何在单元测试中检查进程是否正在运行?
  • 你没有。您应该验证该方法是否仅被调用。这就是您正在测试的视图模型的责任有多远。有关如何执行此操作的示例,请参见我的答案。您需要模拟对静态方法的调用。

标签: c# wpf unit-testing mvvm icommand


【解决方案1】:

您的视图模型不是真正可测试的。如果你愿意,你应该调用静态Process.Start方法调用你注入视图模型的接口的方法调用:

public ViewModel(IProcessLoader processLoader) =>
    _processLoader = processLoader;

//...

private void OpenLink()
{
    try
    {
        _processLoader.Start("https://www.google.com/");
    }
    catch
    {
        // catch error...
    }
}

然后您可以在单元测试中模拟接口,例如使用类似 Moq 的模拟框架:

[TestMethod]
public void TestMoreInfoCommand()
{
    //Arrange
    Mock<IProcessLoader> processLoader = new Mock<IProcessLoader>();
    viewModel vm = new viewModel(processLoader.Object);
    ICommand command = vm.MoreInfoCommand;

    //Act
    command.Execute(null);

    //Assert
    processLoader.Verify(x => x.Start(It.IsAny<string>()));
}

在实际应用中,您可以将接口实现为Process.Start 的包装器:

public interface IProcessLoader
{
    void Start(string s);
}

public class ProcessLoader : IProcessLoader
{
    public void Start(string s) => Process.Start(s);
}

认识到您应该只验证该方法是从视图模型中调用的,这一点很重要。针对视图模型类的单元测试不应测试Process 类的功能。当调用Start 时,视图模型的职责就结束了。

.NET 和底层操作系统负责 Process 类的实际作用。您不应该对此进行测试,而应该只测试您自己的代码。

【讨论】:

  • @Anonymous:这能回答你的问题吗?
【解决方案2】:

vm.MoreInfoCommand.CanExecute(null) 只会在您的RelayCommand 中调用canExecute 方法,但您没有提供任何方法。 CanExecute 没有说明OpenLink 是否会成功执行。

虽然我不是单元测试专家,但如果你的代码看起来像这样,我怀疑是否值得一开始就为它编写单元测试,因为它非常简单。

正如用户 auburg 所说,您可以检查进程是否启动,尽管进程的名称取决于哪个浏览器是标准浏览器。此外,您必须等待一段时间才能开始该过程。并且可能已经运行了同名的进程,有很多麻烦。

如果你真的想要,你可以检查一下著名的模拟框架之一,例如。 Moq 或 Rhino Mocks,能够模拟 Process.Start。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    • 2012-09-15
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    • 2023-04-09
    相关资源
    最近更新 更多