简要:
CodeUI Test 是一个集成在VS2010和更高的版本上的一个公共的自动化测试API,由于它不是.NetFrameWork的一个部分,而是作为VS的一个公共组件,所以对于VS版本的依赖很大,只有在2010的Premiun和Ultimate已经更高的版本中才可以使用,对于这两个版本在安装的过程中,也会出现一些问题,导致安装路径下缺少相关的一些程序集,后期也很容易出现问题。近期由于在接触CodeUI自动化测试工具开发的项目,所以对CodeUI的这一整体技术先在原理和内部实现机制上做一个学习总结。
Agenda:
1. CodeUI 基本实现框图。
2. Sample 基本演示代码。
3.CodeUI 实现代码深度分析。
1. CodeUI 基本实现框图:
2. Sample 基本演示代码:
2.0 CodeUI已经有了一套录制操作步骤生成测试代码的功能,可以将功能测试的一些动作录制并生成测试代码,但生成的往往不会是我们最满意的效果,所以我们需要修改调整,然后执行测试时,能够模拟用户的一些功能更性操作,比如登陆,点击,录入等实现自动化测试。
2.1首先我们创建一个Winform小程序,实现加法的一个简易计算的界面,采用四个操作用例去验证它的界面计算的功能,然后我们新建一个CodeUI测试项目,自动打开我们刚刚写好的winForm小程序,然后在界面录入数据,模拟用例点击的界面用户事件,在每一个操作完成后,添加断言验证预期与实际输出是否一致。
图1:创建的测试winform计算程序
2.2 新建一个CodeUI测试项目:
2.3 打开执行刚才的winForm程序,然后新增测试项目时,选择录制:
2.4 开始录制功能测试的操作,和数据录入:
在 “txt_num1” 文本框 中键入“12345”,在 “txt_num2” 文本框 中键入“45678”, 单击 “Calculate” 按钮
在 “txt_num1” 文本框 中键入“0.123”,在 “txt_num2” 文本框 中键入“12344”, 单击 “Calculate” 按钮
在 “txt_num1” 文本框 中键入“12345.123”,在 “txt_num2” 文本框 中键入“0.123”, 单击 “Calculate” 按钮
在 “txt_num1” 文本框 中键入“1233”,在 “txt_num2” 文本框 中键入“232332”,单击 “Reset” 按钮
生成代码得到的几个重要文件如下:这也是我们最后要深入理解的程序集和文件。
核心源程序:
class="code_img_closed" src="/Upload/Images/2014072815/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('251c801d-999b-4d8c-883f-a103ab5d076b',event)" src="/Upload/Images/2014072815/2B1B950FA3DF188F.gif" alt="" />using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Windows.Input; using System.Windows.Forms; using System.Drawing; using System.Diagnostics; using Microsoft.VisualStudio.TestTools.UITesting; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UITest.Extension; using Keyboard = Microsoft.VisualStudio.TestTools.UITesting.Keyboard; namespace myCodedUITest { /// <summary> /// CodedUITest1 的摘要说明 /// </summary> [CodedUITest] public class CodedUITest1 { // public resource private Process process; private const string path = @"C:\myCodeUISample.exe"; public CodedUITest1() { } [TestMethod] public void CodedUITestMethod1() { process= new Process(); inistallTest(); this.UIMap.RecordedMethod1(); closeTest(); } /// <summary> /// /// </summary> public void closeTest() { process.Close(); } public void inistallTest() { System.Diagnostics.Process.Start(path); } #region 附加测试特性 // 编写测试时,可以使用以下附加特性: ////运行每项测试之前使用 TestInitialize 运行代码 //[TestInitialize()] //public void MyTestInitialize() //{ // // 若要为此测试生成代码,请从快捷菜单中选择“为编码的 UI 测试生成代码”,然后选择菜单项之一。 //} ////运行每项测试之后使用 TestCleanup 运行代码 //[TestCleanup()] //public void MyTestCleanup() //{ // // 若要为此测试生成代码,请从快捷菜单中选择“为编码的 UI 测试生成代码”,然后选择菜单项之一。 //} #endregion /// <summary> ///获取或设置测试上下文,该上下文提供 ///有关当前测试运行及其功能的信息。 ///</summary> public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } private TestContext testContextInstance; public UIMap UIMap { get { if ((this.map == null)) { this.map = new UIMap(); } return this.map; } } private UIMap map; } }myCodedUITest
// ------------------------------------------------------------------------------ // <auto-generated> // 此代码由编码的 UI 测试生成器生成。 // 版本: 12.0.0.0 // // 如果重新生成代码,则更改此文件可能会导致错误的行为, // 并将丢失这些更改。 // </auto-generated> // ------------------------------------------------------------------------------ namespace myCodedUITest { using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Drawing; using System.Text.RegularExpressions; using System.Windows.Input; using Microsoft.VisualStudio.TestTools.UITest.Extension; using Microsoft.VisualStudio.TestTools.UITesting; using Microsoft.VisualStudio.TestTools.UITesting.WinControls; using Microsoft.VisualStudio.TestTools.UnitTesting; using Keyboard = Microsoft.VisualStudio.TestTools.UITesting.Keyboard; using Mouse = Microsoft.VisualStudio.TestTools.UITesting.Mouse; using MouseButtons = System.Windows.Forms.MouseButtons; [GeneratedCode("编码的 UI 测试生成器", "12.0.21005.1")] public partial class UIMap { /// <summary> /// Test The Calculate function is performance well /// </summary> public void RecordedMethod1() { #region Variable Declarations WinEdit uITxt_num1Edit = this.UIForm1Window.UITxt_num1Window.UITxt_num1Edit; WinEdit uITxt_num2Edit = this.UIForm1Window.UITxt_num2Window.UITxt_num2Edit; WinButton uICalculateButton = this.UIForm1Window.UICalculateWindow.UICalculateButton; WinEdit uITxt_sumEdit = this.UIForm1Window.UITxt_sumWindow.UITxt_sumEdit; WinButton uIResetButton = this.UIForm1Window.UIResetWindow.UIResetButton; #endregion // 未录制上一个鼠标操作。 // 在 “txt_num1” 文本框 中键入“12345” uITxt_num1Edit.Text = this.RecordedMethod1Params.UITxt_num1EditText; // 在 “txt_num2” 文本框 中键入“45678” uITxt_num2Edit.Text = this.RecordedMethod1Params.UITxt_num2EditText; // 单击 “Calculate” 按钮 Mouse.Click(uICalculateButton, new Point(19, 18)); AssertMethod1(); // 单击 “txt_sum” 文本框 Mouse.Click(uITxt_sumEdit, new Point(49, 7)); // 在 “txt_num1” 文本框 中键入“0.123” uITxt_num1Edit.Text = this.RecordedMethod1Params.UITxt_num1EditText1; // 在 “txt_num2” 文本框 中键入“12344” uITxt_num2Edit.Text = this.RecordedMethod1Params.UITxt_num2EditText1; // 单击 “Calculate” 按钮 Mouse.Click(uICalculateButton, new Point(45, 14)); AssertMethod1(); // 在 “txt_num1” 文本框 中键入“12345.123” uITxt_num1Edit.Text = this.RecordedMethod1Params.UITxt_num1EditText2; // 在 “txt_num2” 文本框 中键入“0.123” uITxt_num2Edit.Text = this.RecordedMethod1Params.UITxt_num2EditText2; // 单击 “Calculate” 按钮 Mouse.Click(uICalculateButton, new Point(46, 13)); AssertMethod1(); // 单击 “Reset” 按钮 Mouse.Click(uIResetButton, new Point(64, 12)); AssertMethod2(); // 在 “txt_num1” 文本框 中键入“1233” uITxt_num1Edit.Text = this.RecordedMethod1Params.UITxt_num1EditText3; // 在 “txt_num2” 文本框 中键入“232332” uITxt_num2Edit.Text = this.RecordedMethod1Params.UITxt_num2EditText3; // 单击 “Reset” 按钮 Mouse.Click(uIResetButton, new Point(50, 7)); AssertMethod2(); } /// <summary> /// AssertMethod1 - 使用“AssertMethod1ExpectedValues”将参数传递到此方法中。 /// </summary> public void AssertMethod1() { #region Variable Declarations WinEdit uITxt_num1Edit = this.UIForm1Window.UITxt_num1Window.UITxt_num1Edit; WinEdit uITxt_num2Edit = this.UIForm1Window.UITxt_num2Window.UITxt_num2Edit; WinEdit uITxt_sumEdit = this.UIForm1Window.UITxt_sumWindow.UITxt_sumEdit; WinButton uIResetButton = this.UIForm1Window.UIResetWindow.UIResetButton; #endregion // 点击计算后断言 Assert.AreEqual(Double.Parse(uITxt_num1Edit.Text) + Double.Parse(uITxt_num2Edit.Text), Double.Parse(uITxt_sumEdit.Text), 0); } public void AssertMethod2() { #region Variable Declarations WinEdit uITxt_num1Edit = this.UIForm1Window.UITxt_num1Window.UITxt_num1Edit; WinEdit uITxt_num2Edit = this.UIForm1Window.UITxt_num2Window.UITxt_num2Edit; WinEdit uITxt_sumEdit = this.UIForm1Window.UITxt_sumWindow.UITxt_sumEdit; WinButton uIResetButton = this.UIForm1Window.UIResetWindow.UIResetButton; #endregion // 确认 “Reset” 按钮 等于“Reset”的“Name”属性 Assert.AreEqual("", uITxt_num1Edit.Text.Trim() + uITxt_num2Edit.Text.Trim() + uITxt_sumEdit.Text.Trim(), true, "Test Runed Failed"); } #region Properties public virtual RecordedMethod1Params RecordedMethod1Params { get { if ((this.mRecordedMethod1Params == null)) { this.mRecordedMethod1Params = new RecordedMethod1Params(); } return this.mRecordedMethod1Params; } } public virtual AssertMethod1ExpectedValues AssertMethod1ExpectedValues { get { if ((this.mAssertMethod1ExpectedValues == null)) { this.mAssertMethod1ExpectedValues = new AssertMethod1ExpectedValues(); } return this.mAssertMethod1ExpectedValues; } } public UIForm1Window UIForm1Window { get { if ((this.mUIForm1Window == null)) { this.mUIForm1Window = new UIForm1Window(); } return this.mUIForm1Window; } } #endregion #region Fields private RecordedMethod1Params mRecordedMethod1Params; private AssertMethod1ExpectedValues mAssertMethod1ExpectedValues; private UIForm1Window mUIForm1Window; #endregion } /// <summary> /// 要传递到“RecordedMethod1”中的参数 /// </summary> [GeneratedCode("编码的 UI 测试生成器", "12.0.21005.1")] public class RecordedMethod1Params { #region Fields /// <summary> /// 在 “txt_num1” 文本框 中键入“12345” /// </summary> public string UITxt_num1EditText = "12345"; /// <summary> /// 在 “txt_num2” 文本框 中键入“45678” /// </summary> public string UITxt_num2EditText = "45678"; /// <summary> /// 在 “txt_num1” 文本框 中键入“0.123” /// </summary> public string UITxt_num1EditText1 = "0.123"; /// <summary> /// 在 “txt_num2” 文本框 中键入“12344” /// </summary> public string UITxt_num2EditText1 = "12344"; /// <summary> /// 在 “txt_num1” 文本框 中键入“12345.123” /// </summary> public string UITxt_num1EditText2 = "12345.123"; /// <summary> /// 在 “txt_num2” 文本框 中键入“0.123” /// </summary> public string UITxt_num2EditText2 = "0.123"; /// <summary> /// 在 “txt_num1” 文本框 中键入“1233” /// </summary> public string UITxt_num1EditText3 = "1233"; /// <summary> /// 在 “txt_num2” 文本框 中键入“232332” /// </summary> public string UITxt_num2EditText3 = "232332"; #endregion } /// <summary> /// 要传递到“AssertMethod1”中的参数 /// </summary> [GeneratedCode("编码的 UI 测试生成器", "12.0.21005.1")] public class AssertMethod1ExpectedValues { #region Fields /// <summary> /// 确认 “txt_num1” 窗口 等于“txt_num1”的“ControlName”属性 /// </summary> public string UITxt_num1WindowControlName = "txt_num1"; /// <summary> /// 确认 “txt_sum” 窗口 等于“txt_sum”的“ControlName”属性 /// </summary> public string UITxt_sumWindowControlName = "txt_sum"; /// <summary> /// 确认 “Reset” 按钮 等于“Reset”的“Name”属性 /// </summary> public string UIResetButtonName = "Reset"; #endregion } [GeneratedCode("编码的 UI 测试生成器", "12.0.21005.1")] public class UIForm1Window : WinWindow { public UIForm1Window() { #region 搜索条件 this.SearchProperties[WinWindow.PropertyNames.Name] = "Form1"; this.SearchProperties.Add(new PropertyExpression(WinWindow.PropertyNames.ClassName, "WindowsForms10.Window", PropertyExpressionOperator.Contains)); this.WindowTitles.Add("Form1"); #endregion } #region Properties public UITxt_num1Window UITxt_num1Window { get { if ((this.mUITxt_num1Window == null)) { this.mUITxt_num1Window = new UITxt_num1Window(this); } return this.mUITxt_num1Window; } } public UITxt_num2Window UITxt_num2Window { get { if ((this.mUITxt_num2Window == null)) { this.mUITxt_num2Window = new UITxt_num2Window(this); } return this.mUITxt_num2Window; } } public UICalculateWindow UICalculateWindow { get { if ((this.mUICalculateWindow == null)) { this.mUICalculateWindow = new UICalculateWindow(this); } return this.mUICalculateWindow; } } public UITxt_sumWindow UITxt_sumWindow { get { if ((this.mUITxt_sumWindow == null)) { this.mUITxt_sumWindow = new UITxt_sumWindow(this); } return this.mUITxt_sumWindow; } } public UIResetWindow UIResetWindow { get { if ((this.mUIResetWindow == null)) { this.mUIResetWindow = new UIResetWindow(this); } return this.mUIResetWindow; } } #endregion #region Fields private UITxt_num1Window mUITxt_num1Window; private UITxt_num2Window mUITxt_num2Window; private UICalculateWindow mUICalculateWindow; private UITxt_sumWindow mUITxt_sumWindow; private UIResetWindow mUIResetWindow; #endregion } [GeneratedCode("编码的 UI 测试生成器", "12.0.21005.1")] public class UITxt_num1Window : WinWindow { public UITxt_num1Window(UITestControl searchLimitContainer) : base(searchLimitContainer) { #region 搜索条件 this.SearchProperties[WinWindow.PropertyNames.ControlName] = "txt_num1"; this.WindowTitles.Add("Form1"); #endregion } #region Properties public WinEdit UITxt_num1Edit { get { if ((this.mUITxt_num1Edit == null)) { this.mUITxt_num1Edit = new WinEdit(this); #region 搜索条件 this.mUITxt_num1Edit.WindowTitles.Add("Form1"); #endregion } return this.mUITxt_num1Edit; } } #endregion #region Fields private WinEdit mUITxt_num1Edit; #endregion } [GeneratedCode("编码的 UI 测试生成器", "12.0.21005.1")] public class UITxt_num2Window : WinWindow { public UITxt_num2Window(UITestControl searchLimitContainer) : base(searchLimitContainer) { #region 搜索条件 this.SearchProperties[WinWindow.PropertyNames.ControlName] = "txt_num2"; this.WindowTitles.Add("Form1"); #endregion } #region Properties public WinEdit UITxt_num2Edit { get { if ((this.mUITxt_num2Edit == null)) { this.mUITxt_num2Edit = new WinEdit(this); #region 搜索条件 this.mUITxt_num2Edit.WindowTitles.Add("Form1"); #endregion } return this.mUITxt_num2Edit; } } #endregion #region Fields private WinEdit mUITxt_num2Edit; #endregion } [GeneratedCode("编码的 UI 测试生成器", "12.0.21005.1")] public class UICalculateWindow : WinWindow { public UICalculateWindow(UITestControl searchLimitContainer) : base(searchLimitContainer) { #region 搜索条件 this.SearchProperties[WinWindow.PropertyNames.ControlName] = "btn_Calculate"; this.WindowTitles.Add("Form1"); #endregion } #region Properties public WinButton UICalculateButton { get { if ((this.mUICalculateButton == null)) { this.mUICalculateButton = new WinButton(this); #region 搜索条件 this.mUICalculateButton.SearchProperties[WinButton.PropertyNames.Name] = "Calculate"; this.mUICalculateButton.WindowTitles.Add("Form1"); #endregion } return this.mUICalculateButton; } } #endregion #region Fields private WinButton mUICalculateButton; #endregion } [GeneratedCode("编码的 UI 测试生成器", "12.0.21005.1")] public class UITxt_sumWindow : WinWindow { public UITxt_sumWindow(UITestControl searchLimitContainer) : base(searchLimitContainer) { #region 搜索条件 this.SearchProperties[WinWindow.PropertyNames.ControlName] = "txt_sum"; this.WindowTitles.Add("Form1"); #endregion } #region Properties public WinEdit UITxt_sumEdit { get { if ((this.mUITxt_sumEdit == null)) { this.mUITxt_sumEdit = new WinEdit(this); #region 搜索条件 this.mUITxt_sumEdit.WindowTitles.Add("Form1"); #endregion } return this.mUITxt_sumEdit; } } #endregion #region Fields private WinEdit mUITxt_sumEdit; #endregion } [GeneratedCode("编码的 UI 测试生成器", "12.0.21005.1")] public class UIResetWindow : WinWindow { public UIResetWindow(UITestControl searchLimitContainer) : base(searchLimitContainer) { #region 搜索条件 this.SearchProperties[WinWindow.PropertyNames.ControlName] = "btn_Reset"; this.WindowTitles.Add("Form1"); #endregion } #region Properties public WinButton UIResetButton { get { if ((this.mUIResetButton == null)) { this.mUIResetButton = new WinButton(this); #region 搜索条件 this.mUIResetButton.SearchProperties[WinButton.PropertyNames.Name] = "Reset"; this.mUIResetButton.WindowTitles.Add("Form1"); #endregion } return this.mUIResetButton; } } #endregion #region Fields private WinButton mUIResetButton; #endregion } }uimap.designer.cs
2.5 执行测试:
3.CodeUI 实现代码深度分析:
CodeUI Test的核心我认为其实是如何获取测试环境(程序运行环境)上的UI控件,并能够操作它的属性和方法,而且目前支持WPF,Winform……但这些都得力与开放的一些API,下面我们就来看一下。
几个重要的程序集:
Microsoft.VisualStudio.QualityTools.CodedUITestFramework.dll
Microsoft.VisualStudio.TestTools.UITesting.dll -----提供了初始化应用程序界面控件的封装。
ps:我们本次使用的是winform程序,所以为了初始化和获取到界面控件,引入了winControls声明和操作winForm控件属性和方法。
Microsoft.VisualStudio.TestTools.UITest.Extension.dll
Microsoft.VisualStudio.TestTools.UITest.Common.dll ---主要用户Test代码生成相关的类
今天先写到这里吧,明天再做一些更深入的代码补充。