写在最前面

  这个系列的主旨是要跟大家分享一下关于自动化测试框架的构建的一些心得。这几年,做了一些自动化测试框架以及团队的构建的工作。过程中遇到了很多这样的同学,他们在学习了某一门语言和一些自动化测试的类库或者工具之后,打算进一步的提高。我想这个系列也许会帮助到你,我们一起从各个维度来看一看自动化测试框架的一些最佳实践。本人能力有限,如果有什么不正确的的地方还各位大牛指正。

自动化测试 - 设计模式

设计模式(Design pattern)代表了最佳的实践,是针对一些特定场景下问题的解决方案。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。这是网络上比较通用的对设计模式的解释。

那么,对于自动化测试架构来说又有哪些最佳实践呢?

  • 领域驱动设计(Domain Driven Design):用接近终端用户的语言来表达你的测试。
  • 页面对象(Page Objects):基于UI的简单抽象。
  • 组件化(Loadable Component):组件化项目中的待测试元素。
  • 基于命令的测试(Bot Style Tests):基于命令而不是对象的设计,对Page Objects的补充
  • 验收测试(Acceptance Tests):使用粗粒度的UI测试来帮助结构开发工作。
  • 回归测试(Regression Tests):将多个验收测试的操作收集到一个地方以方便维护。

注意:这里的“验收测试”和“回归测试”是指对自动化测试用例的组织方式,而不是通常意义上的测试过程。虽然这也会成为实际测试活动的一部分。

这个系列重要内容之一,就是为大家一一讲解上面的模式。 因此,这一个小节我会放在之后所有关于自动化设计模式问文章开头,因为他真的很重要~~~ 关于上述模式的总结,你在也可以在Selenium项目的Wiki里找到,链接如下: https://github.com/SeleniumHQ/selenium/wiki/Design-Patterns

PageObject模式目的

这一篇,我们要讨论的是上述模式中最有名,也是最被误用最多的模式:Page Objects。简而言之,Page Objects是我们针对页面以及页面相关服务的封装。对于使用者来说,每一个封装好的页面对象都为他们提供了获取页面数据和页面相关的操作方法。让使用者可以简单的像手工操作一样的来书写自动化测试的用例。

我们来回顾一下上一讲中的那个例子:

  1. namespace AutoFramework.TestCase
  2. {
  3. public class TestSuite_Demo : TestBase
  4. {
  5. public TestSuite_Demo(TestContextFixture context, ServiceFixture service, DBFixture database)
  6. : base(context, service, database) { }
  7.  
  8. [Fact(DisplayName = "TestCase.Demo001")]
  9. public void Demo_Case_Create()
  10. {
  11. //-->Data preparation.
  12. var userModel = ModelBuilder.ForJsonFile<UserModel>("DemoCase/TestData.json");
  13.  
  14. //-->Test case exec way 01.
  15. var signInPage = Router.GoTo<SignInPage>();
  16. var homePage = signInPage.SignIn("user-name", "pwd");
  17. var addUserPage = homePage.NavMenu.Select<AddUserPage>("User", "New");
  18. var userListPage = addUserPage.AddUser(userModel);
  19.  
  20. //-->Test case exec way 02.
  21. /*You can custom this 'Workflow'*/
  22. var userListPage = Router.GoTo<SignInPage>()
  23. .SignIn("user-name", "pwd")
  24. .NavMenu.Select<AddUserPage>("User", "New")
  25. .AddUser(userModel);
  26.  
  27. Assert.True(userListPage.IsExistUser(userModel.Name));
  28. }
  29. }
  30. }

其中,signInPage,homePage,addUserPage,userListPage 都是一些页面对象。对于书写Test Case 的人而言,他们可以不需要过多的关心页面上的实现细节。而是,更加关注业务本身。这也是框架设计的目的之一:分层

自动化测试目的主要是模拟手工操作,而很多测试框架比如Selenium,Appnium,white... ...等都提供了基本的驱动支持。在实际自动化构建测试构建过程中,UI改变导致的测试用例运行失败应该是自动化需要解决的首要问题,而PageObjects的好处之一,就是可以较好应对UI的改变对测试用维护成本的影响。

PageObject模式原则

上一个小节,我们看到了如何使用封装好的PageObject对象。那么,应该如何来设计(封装)一个PageObject呢?下面列出了一个些PageObject设计的原则:

  • 提供公共方法来表示页面提供的服务
  • 尽量不要暴露页面的内部细节
  • 一般不要在Page Objects中使用断言(CheckPoint),因为检查点往往是测试用例应当关心的。
  • 提供页面操作的方法返回其他PageObjects
  • Page Objects不需要完整的表达整个页面,也可以是一个组件。
  • 相同操作的如果结果不同,应当创建不同的方法。

还是以登录页面为例:

  1. public class SignInPage : PageObjectBase
  2. {
  3. public SignInPage(IWebDriver driver) : base(driver)
  4. {
  5. WaitSelector.WaitingFor_ElementExists(this.Driver,By.Id("ContentPlaceHolder1_txtUsername"));
  6.  
  7. txtUserName = new TextBox(driver, By.Id("txtUsername"));
  8. txtPassword = new TextBox(driver, By.Id("txtPassword"));
  9. btnSignIn = new Button(driver, By.XPath(".//input[@value='Sign In']"));
  10. }
  11.  
  12. #region Page elements
  13. protected TextBox txtUserName;
  14. protected TextBox txtPassword;
  15. protected Button btnSignIn;
  16. #endregion Page elements
  17.  
  18. #region Action for test case
  19. /// <summary>
  20. /// Sign In
  21. /// </summary>
  22. /// <param name="userName">User name</param>
  23. /// <param name="password">Password</param>
  24. public HomePage SignIn(string userName, string password)
  25. {
  26. this.txtUserName.Clear();
  27. this.txtPassword.Clear();
  28.  
  29. this.txtUserName.SendKeys(userName);
  30. this.txtPassword.SendKeys(password);
  31. this.btnSignIn.Click();
  32.  
  33. return new HomePage(this.Driver);
  34. }
  35.  
  36. public SignInPage SignInError(string userName, string password)
  37. {
  38. this.txtUserName.Clear();
  39. this.txtPassword.Clear();
  40.  
  41. this.txtUserName.SendKeys(userName);
  42. this.txtPassword.SendKeys(password);
  43. this.btnSignIn.Click();
  44.  
  45. return new this(this.Driver);
  46. }
  47. #endregion
  48. }

可以看到,对于登录页面的实现。基本的页面操作(比如登录),如果行为是不同的建议提供两个不同的方法。登录成功返回的是HomePage。但是如果失败了返回的是当前页面。这样的编码方式既清晰,也同时满足了前面例子中的链式调用的简洁。

在方法的内部,我们需要处理页面的等待延时处理,元素查找等一系列与UI相关的操作。所谓Page Objects的封装,就是希望使用Page Objects的人不需要关心页面上的细节。

关于检查点,很多实现是在Page Objects内部提供了一些方法来检查,例如:

  1. public SignInPage CheckErrorMessage(string message)
  2. {
  3. //... ...
  4. Assert.True(message,"xxxx");
  5.  
  6. return new this(this.Driver);
  7. }

我们来思考一下,断言检查也就是检查Check Point应该由是测试用例本身负责,还是由提供页面服务的Page Objects来负责呢?很明显我们不应该在Page Objects内部处理测试用例的检查点。下面的这种处理方式是Page Objects模式本身建议的更好的做法:

让我们以下面的这个UI为例:

  1. public string GetErrorMessage()
  2. {
  3. //返回页面上的错误信息
  4. }
  5.  
  6. //调用代码
  7. ...
  8. var signInPage = Router.GoTo<SignInPage>();
  9. var errorMsg = signInPage.SignIn("user-name", "pwd").GetErrorMessage();
  10. Assert.Equal(errorMsg,"用户名密码错误!");

设计模式的使用都有它的场景的意图。Page Objects模式的设计意图主要就是为了解决一下几个问题:

  • 重用操作
  • 使得自动化测试用例的编写不用关心过多的UI相关的细节
  • 应对UI的变化

细心的同学也许已经注意到了,UI的操作我们并没有直接是用Selenium的WebElement,而是做了一些封装。使用了类似Button,TextBox 这样的对象。这一部分涉及到的是我们后面要讲到的另一个设计模式Loadable Component 和 Bot Style Tests, 后面的课程会对这个专门进行讲解。

这一篇就先到这里啦~~,自动化测试框架的构建是需要一定知识基础和自动化测试经验的。希望我们能在接下来的这段时间里一起提高,但愿我的观点对你有所帮助~~~

小北De系列文章:

  《[小北De编程手记] : Selenium For C# 教程

  《[小北De编程手记]:玩转 xUnit.Net》(未完成)

如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!
作者:小北@North
来源:http://www.cnblogs.com/NorthAlan
声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。

[小北De编程手记] [Lesson 02] AutoFramework构建 之 Page Objects - 设计模式的更多相关文章

  1. [小北De编程手记] Lesson 01 - AutoFramework构建 之 从一个简单的Demo聊起

    写在最前面 这个系列的主旨是要跟大家分享一下关于自动化测试框架的构建的一些心得.这几年,做了一些自动化测试框架以及团队的构建的工作.过程中遇到了很多这样的同学,他们在学习了某一门语言和一些自动化测试的 ...

  2. [小北De编程手记] : Lesson 02 - Selenium For C# 之 核心对象

    从这一篇开始,开始正式的介绍Selenium 以及相关的组件,本文的将讨论如下问题: Selenium基本的概念以及在企业化测试框架中的位置 Selenium核心对象(浏览器驱动) Web Drive ...

  3. [小北De编程手记] : Lesson 02 玩转 xUnit.Net 之 基本UnitTest & 数据驱动

    关于<玩转 xUnit.Net>系列文章,我想跟大家分享的不是简单的运行一下测试用例或是介绍一下标签怎么使用(这样的文章网上很多).上一篇<Lesson 01 玩转 xUnit.Ne ...

  4. [小北De编程手记] : Lesson 08 - Selenium For C# 之 PageFactory & 团队构建

    本文想跟大家分享的是Selenium对PageObject模式的支持和自动化测试团队的构建.<Selenium For C#>系列的文章写到这里已经接近尾声了,如果之前的文章你是一篇篇的读 ...

  5. [小北De编程手记] : Lesson 07 - Selenium For C# 之 窗口处理

    在实际的自动化测试过程中,我们会遇见许多需要对窗口进行处理的情况.比如,点击删除某条信息的时候系统会显示一个Alert框.或者点击某个超链接时会在浏览器中打开一个新的页面.这一篇,来和大家分享一下Se ...

  6. [小北De编程手记] : Lesson 06 - Selenium For C# 之 流程控制

    无论你是用哪一种自动化测试的驱动框架,当我们构建一个复杂应用程序的自动化测试的时候.都希望构建一个测试流程稳定,维护成本较低的自动化测试.但是,现实往往没有理想丰满.而这一篇,我会为大家讲解我们在使用 ...

  7. [小北De编程手记] : Lesson 01 - Selenium For C# 之 环境搭建

    在我看来一个自动化测试平台的构建,是一种很好的了解开发语言,单元测试框架,自动化测试驱动,设计模式等等等的途径.因此,在下选择了自动化测试的这个话题来和大家分享一下本人关于软件开发和自动化测试的认识. ...

  8. [小北De编程手记] : Lesson 03 - Selenium For C# 之 元素定位

    无论哪一种自动化测试的驱动框架(基于B/S,桌面应用,还是手机App).都应当具有一套优秀的元素定位技术.通常的自动化测试流程也可以简单的归结为是一个从被测试程序中识别或是定位元素以及执行操作和验证元 ...

  9. [小北De编程手记] : Lesson 04 - Selenium For C# 之 API 上

    这一部分,我准备向大家介绍Selenium WebDriver的常用API,学习这部分内容需要大家最好有一些简单的HTML相关知识,本文主要涉及到以下内容: Selenium API:元素检查 Sel ...

随机推荐

  1. 【LINUX】主进程、父进程、子进程、守护进程的概念

    一.摘要 详解父进程.子进程.守护进程的区别,例子稍候补充 二.定义区别 主进程 程序执行的入口,可以理解为常用的main 函数 父进程 对于子进程而言, 子进程的创造者,可有多个子进程. 任何进程都 ...

  2. Android中的服务

    Android中的服务 四大组件都是运行在主线程 Android中的服务,是在后台运行 .可以理解成是在后台运行并且是没有界面的Activity. Foreground process 前台进程 ,用 ...

  3. html+css3实现长方体效果

    网上大都是正方体的效果,由于做一个东西需要,写了一个HTML+css3实现的长方体,有需要的也可以看看.                   2017-07-25         21:30:23 h ...

  4. 百度之星2017初赛A轮 1001 小C的倍数问题

    小C的倍数问题 Accepts: 1990 Submissions: 4931 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/3 ...

  5. 使用xlrd模块从excel文件中导入数据

  6. Spring思维导图(MVC篇)

    写在前面 生活就像海洋,只有意志坚强的人才能到达彼岸. 已经很久没有发文章了呀,想必大家都挂念我了,哈哈. 温故而知新,今天一起来复习一下spring mvc的内容吧. spring mvc简介与运行 ...

  7. Eclipse中的Debug

    一.Debug的基本过程 设置断点(双击待设断点左边行号处) 进入Debug模式(在待调试类上右键>调试方式,根据需求选择) 开始调试 二.Debug中的常用操作 继续执行[F8]:继续运行程序 ...

  8. HTML5 — Wed SQL 本地数据库示例

    1 <!DOCTYPE html>2 <html lang="en">3 <head>4 <meta charset="UTF- ...

  9. MySQL进程处于Waiting for table flush的分析

      最近遇到一个案例,很多查询被阻塞没有返回结果,使用show processlist查看,发现不少MySQL线程处于Waiting for table flush状态,查询语句一直被阻塞,只能通过K ...

  10. JS中的变量和输入输出

    一.使用JS的三种方式 1.在HTML标签中,直接内嵌JS(并不提倡使用) <button onclick="alert('点你咋地')">点我</button& ...