单元测试ICommand和方法

时间:2020-05-26 12:52:34

标签: c# wpf unit-testing mvvm icommand

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

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...
    }
}

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

UnitTest

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

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

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

谢谢。

2 个答案:

答案 0 :(得分: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类的实际作用。您不应该对此进行测试,而只能测试自己的代码。

答案 1 :(得分:0)

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

尽管我不是单元测试专家,但是如果您的代码真正像这样,我怀疑是否值得一开始为其编写单元测试,因为它是如此简单。

根据auburg用户的评论,您可以检查进程是否启动,尽管进程的名称将取决于标准浏览器是哪个浏览器。另外,您将需要等待一段时间才能开始该过程。也许已经有一个同名的进程正在运行,有很多麻烦的机会。

如果您确实需要,可以检查是否为众所周知的模拟框架之一,例如Moq或Rhino Mocks,可以嘲笑Process.Start。