wap网站开发语言,华为云软件开发平台,新手怎么学网络运营,crm系统分为哪三类上一章《回忆一下我们的登录逻辑,主要有以下4点:当用户名或密码为空时, 是不允许登录的(登录按钮处于禁用状态).用户名或密码不正确时, 显示用户名或密码不正确的消息框.用户名输入waku, 并且密码输入123用户名或密码为空时, 是不允许登录的(登录按钮处于禁用状态).用户名或密码不正确时, 显示用户名或密码不正确的消息框.用户名输入waku, 并且密码输入123, 登录成功窗口关闭, 回到主窗口.点击登录窗口右上角的X按钮,整个应用程序退出.那么我们就尝试编写代码来进行测试吧.这里我们只测试ViewModel中的逻辑是否正确,对于UI测试则是另一个话题了,以后有机会再写.创建测试工程VS2019支持三种测试框架: MSTest, Nunit和xUnit, 功能上差不多, 你可以选择一个你喜欢的. 这里我们使用xUnit.新建一个名为StyletBookStore.Test的xUnit Test Project(.NET Core)工程:然后对测试工程进行以下操作:添加对StyletBookStore工程的引用, 这是我们测试的对象添加Moq包,我们使用Moq模拟一些Stylet的组件Install-Package Moq -Version 4.13.1添加Shouldly包,方便我们写Assert代码Install-Package Shouldly -Version 3.0.2在StyletBookStore.Test工程中新建一个名为LoginViewModelTest的类, 在其中编写测试代码.配置Stylet的IoC容器因为我们的LoinViewModel使用了依赖注入,所以在测试代码中最好也是使用IoC来创建测试对象.在LoginViewModelTest的构造方法中增加以下代码:public LoginViewModelTest()
{// 向Stylet的IoC中注册服务var builder new StyletIoCBuilder();builder.BindLoginViewModel().ToSelf();_container builder.BuildContainer();
}Stylet的IoC容器需要使用StyletIoCBuilder提供的API来创建, 所以首先我们创建了StyletIoCBuilder的实例.使用BindT范型方法注册服务, 这里我们将LoginViewModel的自身注册进去.更多关于Stylet的IoC配置方法请浏览WIKI最后使用BuildContainer方法创建IoC容器, 由于我们需要在测试方法中使用该容器,所以需要定义一个成员变量来存储它:private readonly IContainer _container;测试功能点: 当用户名或密码为空时, 是不允许登录的(登录按钮处于禁用状态).先增加一个测试方法, 用来测试密码未输入时, CanLogin应该返回false:/// summary
/// 密码未输入, 不允许点击登录
/// /summary
[Fact]
public void CanLoginTest_NoPassword()
{// Arrangevar vm _container.GetLoginViewModel();vm.UserName waku;vm.Password String.Empty;// Actbool canLogin vm.CanLogin;// AssertcanLogin.ShouldBe(false);
}测试用户名未输入和用户名和密码都输入的代码类似, 这里就不再详细说明了, 可直接看代码.Arrange: 设置测试对象并准备测试的先决条件Act: 执行测试的实际工作Assert: 验证结果xUnit要求所有测试方法需要有[Fact]属性.我们在测试方法中遵循AAA模式, 即Arrange, Act和Assert:使用Stylet的IoC容器取得LoginViewModel实例因为用户名和密码都是公有属性, 所以我们直接通过代码来修改它们.使用Shouldly提供的扩展方法ShouldBe来验证canLogin的值测试功能点: 用户名或密码不正确时, 显示用户名或密码不正确的消息框.因为登录逻辑中使用了IWindowManager来显示消息框, 这里我们需要利用Moq来模拟它.在LoginViewModelTest构造方法中增加以下代码:public LoginViewModelTest()
{// 使用Moq虚拟IWindowManager_mockWindowManager new MockIWindowManager();_mockWindowManager.Setup(_showMessageBoxExpr).Returns(MessageBoxResult.OK);...builder.BindIWindowManager().ToInstance(_mockWindowManager.Object); // 注册IWindowManager...
}有了Mock对象, 我们就可以来编写验证登录逻辑的测试代码了:/// summary
/// 用户名错误
/// /summary
[Fact]
public void LoginTest_WrongUserName()
{// Arrangevar vm _container.GetLoginViewModel();vm.UserName wrong_username;vm.Password 123;// Actvm.Login();// Assert_mockWindowManager.Verify(_showMessageBoxExpr, Times.Once); // 应该显示消息框
}还需要测试用户名正确但是密码不正确的情形, 就不详细说明了.我们设置了一个错误的用户名wrong_username.调用了LoginViewModel的Login方法.使用Moq对象的Verify方法来验证模拟方法被调用了. Times.Once代表只调用了一次, 如果未调用或调用次数不是一次, Veryify方法会抛出异常.使用new MockT来创建一个Mock对象, T即是要Mock的实际类型. 后续我们需要使用Mock对象_mockWindowManager, 所以将其定义为一个成员变量:private readonly MockIWindowManager _mockWindowManager;我们使用Moq的Setup方法来为指定的接口模拟一个方法, 该方法接收一个Expression类型的值. 为了简洁性, 我们将Expression定义为一个成员变量:private readonly ExpressionFuncIWindowManager, MessageBoxResult _showMessageBoxExpr wm wm.ShowMessageBox(用户名或密码不正确, 登录失败, MessageBoxButton.OK, MessageBoxImage.Exclamation, MessageBoxResult.None, MessageBoxResult.None, null, null, null);可以看出, 该Expression的定义和我们在Login方法中调用的形式是一致的.Moq的Expression不允许使用可选参数, 所以这里我们将ShowMessageBox的全部参数都明确写出来.关于Moq的详细说明可浏览这里.将模拟的IWindowManager注册进IoC容器中, 这里使用了ToInstance来进行实例注册. 通过Mock对象的Object属性可以取得模拟对象.测试功能点: 用户名输入waku, 并且密码输入123, 点击登录按钮, 登录窗口关闭, 回到主窗口.在Login方法中, 当验证用户名和密码成功后, 我们使用了RequestClose(true)来请求关闭窗口. 我们怎么来测试窗口关闭呢?先看一下Stylet的RequestClose是如何实现的:/// summary
/// Request that the conductor responsible for this screen close it
/// /summary
/// param namedialogResultDialogResult to return, if this is a dialog/param
public virtual void RequestClose(bool? dialogResult null)
{var conductor this.Parent as IChildDelegate;if (conductor ! null){this.logger.Info(RequstClose called. Conductor: {0}; DialogResult: {1}, conductor, dialogResult);conductor.CloseItem(this, dialogResult);}else{var e new InvalidOperationException(String.Format(Unable to close ViewModel {0} as it must have a conductor as a parent (note that windows and dialogs automatically have such a parent), this.GetType()));this.logger.Error(e);throw e;}
}所以解决方案就出来了:Mock相关的代码如下, 与MockIWindowManager类似:public class LoginViewModelTest
{...private readonly MockIWindowManager _mockWindowManager;...public LoginViewModelTest(){...// 使用Moq虚拟IChildDelegate_mockChildDelegate new MockIChildDelegate();...builder.BindIChildDelegate().ToInstance(_mockChildDelegate.Object); // 注册IChildDelegate...}测试方法:/// summary
/// 正确的用户名和密码
/// /summary
[Fact]
public void LoginTest()
{// Arrangevar vm _container.GetLoginViewModel();var childDelegate _container.GetIChildDelegate();vm.UserName waku;vm.Password 123;vm.Parent childDelegate;// Actvm.Login();// Assert_mockWindowManager.Verify(_showMessageBoxExpr, Times.Never); // 不应该显示消息框_mockChildDelegate.Verify(cd cd.CloseItem(vm, true), Times.Once); // 应该关闭窗口,并返回true
}我们只需要验证CloseItem被正确调用即可, 至于窗口是否能关闭那是Stylet需要确保的事了:)使用Times.Never指定模拟的方法不应该被调用.(登录验证成功, 不显示消息框)验证CloseItem(LoginViewModel, true)被调用了一次.首先取得ViewModel的Parent, 这是一个实现了IChildDelegate的对象. 如未取到, 直接抛出异常.否则调用IChildDelegate.CloseItem方法, 将自身和窗口返回值做为参数传递进去.使用Moq来模拟一个IChildDelegate对象.Setup一个CloseItem(LoginViewModel, true)方法.将测试对象LoginViewModel的Parent设置为该模拟对象.测试功能点: 点击登录窗口右上角的X按钮,整个应用程序退出.首先我们回忆一下该功能的代码是怎么写的:protected override void OnViewLoaded()
{var loginViewModel _container.GetLoginViewModel();var result _windowManager.ShowDialog(loginViewModel);if (result ! true){RequestClose();}
}接下来还有一个问题, 不知道你有没有注意到, 就是OnViewLoaded是一个protected方法, 我们不能在测试代码中直接调用ShellViewModel.OnViewLoaded, 那么该怎么办呢? 我们的Act该怎么写呢?这里介绍一个常用的技巧, 我们创建一个类继承ShellViewModel的类, 定义一个public方法, 并在该方法中调用ShellViewModel.OnViewLoaded. 因为该类是ShellViewModel的子类, 所以ShellViewModel的protected方法也可在子类中调用.代码如下:/// summary
/// 为了测试ShellViewModel.OnViewLoaded方法而创建的类
/// /summary
public class ShellViewModelForTest : ShellViewModel
{public ShellViewModelForTest(IContainer container, IWindowManager windowManager) : base(container, windowManager){}public void LoadView(){base.OnViewLoaded();}
}至于其它的测试与Login中基本类似, 详细的请看代码.该功能是在ShellViewModel的OnViewLoaded方法中实现的,所以这是Shell中的功能, 所以我们需要创建一个新的测试类ShellViewModelTest, 来测试该功能.OnViewLoaded方法中同样也使用了IWindowManager, 和RequestClose方法, 所以那些Moq的东西也少不了.至此, 我们的测试代码就写完了. 可以看出使用MVVM模式, 对于界面逻辑的测试是很简单的. 这也是MVVM备受推崇的原因.本篇到此为止, 希望朋友们能多多留言. 源码托管在GITHUB上.Happy Coding~