通过一段时间的B/S后台工作,一定程度上熟悉的单元测试的编写,另外,实际感受到了单元测试的作用。
现在考虑将单元测试应用到C/S客户端上,以保障客户端的长期稳定性。
客户端测试分为两种:服务端协议测试,客户端界面功能测试。协议测试比较简单,就是和服务端通信,测试通信,测试通信结果是否正确;界面测试则比较复杂。
一、服务端协议测试。
项目类型:Unit Test Project
测试流程:
二、客户端界面功能测试。
项目类型:Coded UI Test Project(.Net Framework4.0以上支持)
测试流程:
输出目录:
AppDomain.CurrentDomain.BaseDirectory=D:\SoftwareProjects\TrunkClient2017\TestResults\caiwanwei_DESKTOP-CLV0P90 2018-03-28 14_32_56\Out
项目的目录是:D:\SoftwareProjects\TrunkClient2017 2\Test\Framework.CodedUITest
也就是说,每次测试程序运行都会生成一个输出目录,用当前[用户名_主机名 时间]格式命名。
用Process.Start运行客户端碰到的问题:
1.注册码文件路径问题
Environment.CurrentDirectory不是运行的Exe的路径,不能用,要改为AppDomain.CurrentDomain.BaseDirectory
2.WPF图片路径问题
在代码中设置的商标图片路径改为UriKind.Absolute
ImageLogo.ImageSource = new BitmapImage(new Uri("Images\\Login\\" + name, UriKind.Relative));
改成:
ImageLogo.ImageSource = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "\\Images\\Login\\" + name, UriKind.Absolute));
实际测试过程:
新建Coded UI Test Project的时候会自动创建一个CodedUITest1
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows.Input;
using System.Windows.Forms;
using System.Drawing;
using Microsoft.VisualStudio.TestTools.UITesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UITest.Extension;
using Keyboard = Microsoft.VisualStudio.TestTools.UITesting.Keyboard;
namespace Framework.CodedUITest
{
/// <summary>
/// Summary description for CodedUITest1
/// </summary>
[CodedUITest]
public class CodedUITest1
{
public CodedUITest1()
{
}
[TestMethod]
public void CodedUITestMethod1()
{
// To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items.
}
#region Additional test attributes
// You can use the following additional attributes as you write your tests:
Use TestInitialize to run code before running each test
//[TestInitialize()]
//public void MyTestInitialize()
//{
// // To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items.
//}
Use TestCleanup to run code after each test has run
//[TestCleanup()]
//public void MyTestCleanup()
//{
// // To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items.
//}
#endregion
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
private TestContext testContextInstance;
}
}
并打开一个录制的工具栏
OK=>
这个界面关闭后想打开可以通过Test菜单打开
注意要将编辑光标放在一个[TestMethod]函数中,不然会提示:
录制结果就是插入到这个光标的地方。
按钮功能分别是:开始录制/停止录制,查看录制的步骤,添加控制,生成代码。
0.手动启动服务端。
1.手动打开客户端。
2.点击开始录制。
3.在客户端界面上做一些操作:设置参数,点击登录,退出客户端。
注意:因为客户端是用管理员权限启动的,测试时VS也要用管理员权限启动,不然无权操作客户端程序。
我是在开始菜单操作的,右键VS程序,选择以管理员身份运行。
不然会出现错误:
4.暂停录制,查看一下录制的步骤,没问题的话就生成代码。
5.点击生成代码。
把名称改为LoginUserError
6.查看生成的代码(或者回到第2步继续录制新的操作)
CodedUITest1里面就多了个测试函数
[TestMethod]
public void CodedUITestMethod1()
{
// To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items.
this.UIMap.LoginUserError();
}
继续录制新的操作生成的新代码会还是添加在CodedUITestMethod1中,刚刚的LoginSucess后面,并不会生成一个新的测试函数。
双击UIMap.uitest文件(要先关闭录制窗口)。
UIMap的代码
[GeneratedCode("Coded UITest Builder", "14.0.23107.0")]
public partial class UIMap
{
/// <summary>
/// LoginUserError - Use 'LoginUserErrorParams' to pass parameters into this method.
/// </summary>
public void LoginUserError()
{
#region Variable Declarations
WpfComboBox uIIPComboBox = this.UI用户登录Window.UIIPComboBox;
WpfEdit uIPART_EditableTextBoxEdit = this.UI用户登录Window.UIIPComboBox.UIPART_EditableTextBoxEdit;
WpfEdit uIPortEdit = this.UI用户登录Window.UIPortEdit;
WpfComboBox uIUserComboBox = this.UI用户登录Window.UIUserComboBox;
WpfEdit uIPasswordEdit = this.UI用户登录Window.UIPasswordEdit;
WpfCheckBox uI记住密码CheckBox = this.UI用户登录Window.UI记住密码CheckBox;
WpfButton uI登录Button = this.UI用户登录Window.UI登录Button;
WpfButton uI确定Button = this.UI提示Window.UI确定Button;
#endregion
// Select '127.0.0.1' in 'IP' combo box
uIIPComboBox.EditableItem = this.LoginUserErrorParams.UIIPComboBoxEditableItem;
// Type '{Tab}' in 'PART_EditableTextBox' text box
Keyboard.SendKeys(uIPART_EditableTextBoxEdit, this.LoginUserErrorParams.UIPART_EditableTextBoxEditSendKeys, ModifierKeys.None);
// Type '60005' in 'Port' text box
uIPortEdit.Text = this.LoginUserErrorParams.UIPortEditText;
// Select 'admin2' in 'User' combo box
uIUserComboBox.EditableItem = this.LoginUserErrorParams.UIUserComboBoxEditableItem;
// Move 'Password' text box
Mouse.StartDragging(uIPasswordEdit, new Point(54, 13));
Mouse.StopDragging(uIPasswordEdit, -49, 2);
// Type '********' in 'Password' text box
Keyboard.SendKeys(uIPasswordEdit, this.LoginUserErrorParams.UIPasswordEditSendKeys, true);
// Select '记住密码' check box
uI记住密码CheckBox.Checked = this.LoginUserErrorParams.UI记住密码CheckBoxChecked;
// Click '登录' button
Mouse.Click(uI登录Button, new Point(72, 19));
// Click '确定' button
Mouse.Click(uI确定Button, new Point(38, 24));
}
/// <summary>
/// LoginPortError - Use 'LoginPortErrorParams' to pass parameters into this method.
/// </summary>
public void LoginPortError()
{
#region Variable Declarations
WpfComboBox uIIPComboBox = this.UI用户登录Window.UIIPComboBox;
WpfEdit uIPortEdit = this.UI用户登录Window.UIPortEdit;
WpfComboBox uIUserComboBox = this.UI用户登录Window.UIUserComboBox;
WpfEdit uIPART_EditableTextBoxEdit = this.UI用户登录Window.UIUserComboBox.UIPART_EditableTextBoxEdit;
WpfEdit uIPasswordEdit = this.UI用户登录Window.UIPasswordEdit;
WpfCheckBox uI记住密码CheckBox = this.UI用户登录Window.UI记住密码CheckBox;
WpfButton uI登录Button = this.UI用户登录Window.UI登录Button;
WpfButton uI确定Button = this.UI提示Window.UI确定Button;
#endregion
// Select '127.0.0.1' in 'IP' combo box
uIIPComboBox.EditableItem = this.LoginPortErrorParams.UIIPComboBoxEditableItem;
// Type '60001' in 'Port' text box
uIPortEdit.Text = this.LoginPortErrorParams.UIPortEditText;
// Select 'admin' in 'User' combo box
uIUserComboBox.EditableItem = this.LoginPortErrorParams.UIUserComboBoxEditableItem;
// Type '{Tab}' in 'PART_EditableTextBox' text box
Keyboard.SendKeys(uIPART_EditableTextBoxEdit, this.LoginPortErrorParams.UIPART_EditableTextBoxEditSendKeys, ModifierKeys.None);
// Type '********' in 'Password' text box
Keyboard.SendKeys(uIPasswordEdit, this.LoginPortErrorParams.UIPasswordEditSendKeys, ModifierKeys.Control, true);
// Type '********' in 'Password' text box
Keyboard.SendKeys(uIPasswordEdit, this.LoginPortErrorParams.UIPasswordEditSendKeys1, true);
// Select '记住密码' check box
uI记住密码CheckBox.Checked = this.LoginPortErrorParams.UI记住密码CheckBoxChecked;
// Click '登录' button
Mouse.Click(uI登录Button, new Point(82, 26));
// Click '确定' button
Mouse.Click(uI确定Button, new Point(34, 15));
}
#region Properties
public virtual LoginUserErrorParams LoginUserErrorParams
{
get
{
if ((this.mLoginUserErrorParams == null))
{
this.mLoginUserErrorParams = new LoginUserErrorParams();
}
return this.mLoginUserErrorParams;
}
}
public virtual LoginPortErrorParams LoginPortErrorParams
{
get
{
if ((this.mLoginPortErrorParams == null))
{
this.mLoginPortErrorParams = new LoginPortErrorParams();
}
return this.mLoginPortErrorParams;
}
}
public UI用户登录Window UI用户登录Window
{
get
{
if ((this.mUI用户登录Window == null))
{
this.mUI用户登录Window = new UI用户登录Window();
}
return this.mUI用户登录Window;
}
}
public UI提示Window UI提示Window
{
get
{
if ((this.mUI提示Window == null))
{
this.mUI提示Window = new UI提示Window();
}
return this.mUI提示Window;
}
}
#endregion
#region Fields
private LoginUserErrorParams mLoginUserErrorParams;
private LoginPortErrorParams mLoginPortErrorParams;
private UI用户登录Window mUI用户登录Window;
private UI提示Window mUI提示Window;
#endregion
}
后面熟练了应该是可以考虑手动写这些代码的吧。
7.在VS中运行测试用例。
结果就是将刚刚手动的操作自动执行了一遍,没问题,就算测试成功了。