我已经开发了使用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时会发生什么。
我知道可以直接测试私有方法,但是在我的单元测试中,我也应该检查功能,如果可以,我该如何做。
谢谢。
答案 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。