本文想跟大家分享的是Selenium对PageObject模式的支持和自动化测试团队的构建。《Selenium For C#》系列的文章写到这里已经接近尾声了,如果之前的文章你是一篇篇的读下来并动手实践的话,我相信你应该可以模拟日常工作中80%常见的手动测试用例了。请注意:我的用词是模拟用例,而不是书写自动化测试用例。一个企业级的自动化测试的构建不是单靠Selenium一种技术就能Hold住的。所谓模拟指的是只能实现自动化的某个Case,但是不能工程化的使用。在本人所接触过几家公司的自动化测试的实践中,有很多没有或着不是很合理分层和封装的自动化测试框架。Test Case中操作页面驱动的Code随处看见,无法根据人员的技术能力来分工,无法切换测试环境,只能使用单一的浏览器测试,对QA的使用门槛要求较高… …等等问题。
本文将会介绍如下内容:
Page Object是业界比较流行的自动化测试设计模式,能够有效的提高自动化测试的可维护性和代码的复用率。几乎所有的自动化测试驱动框架都提供了对该模式的支持(Appium ,QTP ,White等)。当然,即使你使用的驱动不支持,我们也完全可以编写代码来实现它。毕竟所谓设计模式不过代码的组织和协作方式而已。
什么是Page Object模式呢?简单的说,就是把待测试应用程序的页面封装成一个对象,暴露相关的属性和方法给使用者(具体测试用例的编写人员)。下图是针对页面的封装:
可以看到,Page类提供了一个TitleText属性向用户返回页面的Title文本,还提供基本的页面操作。对于使用者来说,他们不必关心这个页 面上的元素是如何定位,也不必关心内部使用了怎样的技术实现(Selenium,还是QTP)。另一个好处就是如果开发对页面做了元素做了一定的调 整,我们只需要处理PageObject内部的代码实现,而不需要修改实际测试用例代码。该模式的好处还有很多,本文就不赘述了。我会在之后的框架实践相关的文章中进行描述。
下面我们一起来看一下,Selenium 对PageObject模式的支持。下面的代码是一个登录页面的简单封装:
1 public class SignInPage 2 { 3 public IWebDriver Driver { get; set; } 4 public DashboardSignInPage(IWebDriver driver) 5 { 6 this.Driver = driver; 7 PageFactory.InitElements(driver, this); 8 } 9 #region Page elements 10 [FindsBy(How = How.Id, Using = "username")] 11 protected IWebElement txtUserName; 12 13 [FindsBy(How = How.Id, Using = "password")] 14 protected IWebElement txtPassword; 15 16 [FindsBy(How = How.XPath, Using = ".//button[text()='Sign in']")] 17 protected IWebElement btnSignIn; 18 #endregion Page elements 19 20 #region Action for test case 21 /// <summary> 22 /// Sign In for Dashboard 23 /// </summary> 24 /// <param name="userName">User name</param> 25 /// <param name="password">Password</param> 26 public void SignIn(string userName, string password) 27 { 28 this.txtUserName.Clear(); 29 this.txtPassword.Clear(); 30 31 this.txtUserName.SendKeys(userName); 32 this.txtPassword.SendKeys(password); 33 34 this.btnSignIn.Click(); 35 } 36 37 //Other action... ... 38 #endregion 39 }
上面的代码中出现的FindsBy和PageFactory正是Selenium WebDriver提供的针对PageObject模式的支持。
FageFactory提供了PageObject实例的能力,我们可以看到它本身提供了很多的方法来构建具体的页面类的实例。它的主要作用的是将我们用FindsBy属性标记的字段和指定的DOM元素进行映射。而后我们可以直接使用具体属性操作DOM元素,不必使用FindElement方法来定位。
1 public sealed class PageFactory 2 { 3 public static T InitElements<T>(IElementLocator locator); 4 public static T InitElements<T>(IWebDriver driver); 5 public static void InitElements(ISearchContext driver, object page); 6 public static void InitElements(object page, IElementLocator locator); 7 public static void InitElements(ISearchContext driver, object page, IPageObjectMemberDecorator decorator); 8 public static void InitElements(object page, IElementLocator locator, IPageObjectMemberDecorator decorator); 9 }
FindsBy属性是用来标记程序中的元素是如何定位的。我们可以使用之前《Lesson 03 - Selenium For C# 之 元素定位》中讲到的任何一种定位方式来标记如何定位元素。利用How参数指定定位方式,这是Using参数标识具体的值。如果对如何定位元素不是很清楚,可以查看之前的文章,这里就不再赘述了。
1 // Summary: 2 // Provides the lookup methods for the FindsBy attribute (for using in PageObjects) 3 public enum How 4 { 5 // Summary: 6 // Finds by OpenQA.Selenium.By.Id(System.String) 7 Id = 0, 8 // 9 // Summary: 10 // Finds by OpenQA.Selenium.By.Name(System.String) 11 Name = 1, 12 // 13 // Summary: 14 // Finds by OpenQA.Selenium.By.TagName(System.String) 15 TagName = 2, 16 // 17 // Summary: 18 // Finds by OpenQA.Selenium.By.ClassName(System.String) 19 ClassName = 3, 20 // 21 // Summary: 22 // Finds by OpenQA.Selenium.By.CssSelector(System.String) 23 CssSelector = 4, 24 // 25 // Summary: 26 // Finds by OpenQA.Selenium.By.LinkText(System.String) 27 LinkText = 5, 28 // 29 // Summary: 30 // Finds by OpenQA.Selenium.By.PartialLinkText(System.String) 31 PartialLinkText = 6, 32 // 33 // Summary: 34 // Finds by OpenQA.Selenium.By.XPath(System.String) 35 XPath = 7, 36 // 37 // Summary: 38 // Finds by a custom OpenQA.Selenium.By implementation. 39 Custom = 8, 40 }
那么最后,我们来看一下消费者(上层的测试用例编写人员)将如何使用如何使用这个类,这个Code只是一个使用SignInPage的示例,我们看到这里还是在测试用例中写入了创建Driver和导航页面的代码,按照先前的架构这样也是存在问题的,这里就不再展开了,期待我后续关于自动化框架设计的文章吧~~~:
1 private const string cst_DisplayName = "BaseCheck.SignIn"; 2 [Fact(DisplayName = cst_DisplayName + ".Success")] 3 public void SignIn_Success() 4 { 5 var driver = new FrieFoxDriver(); 6 driver.Url = "www.xxx.com/signin"; 7 8 var signInPage = new SignInPage(driver); 9 signInPage.SignIn("your name", "password"); 10 11 //省略Code... ... 12 driver.Close(); 13 }
读到这里,即使你是初学者。想必也能明白一些PageObject设计模式为何物,这里我还是描述一下 PageObject的意图:PageObject模式是为了封装页面元素定位,页面等待、跳转等操作页面相关的逻辑。使得使用者在不必关心这些逻辑的情 况下,可以书写相关的测试用例。我们回顾一下我之前在《Lesson 02 - Selenium For C# 之 核心对象》中提到的一般的企业级测试框架中的结构图:
上图中的红线圈出来的部分就是PageObject的消费者(编写测试用例的Tester)。可以看到,下一层的开发人员可以把页面的封装成 PageObject,上层的使用者不必关心具体的实现技术是哪一种(Selenium , Appium... ...)。PageObject的消费者可以专注于Test Case的逻辑实现。
之前有很多公司或是朋友跟我探讨过如何组建自动化测试团队。无一例外都谈到了(纠结于)这些问题:需要QA Team有很强的编码能力,他们要学习各种驱动(Selenium , QTP , Appium... ...)的使用 , 了解一些JavaScript、HTML、CSS相关的知识,具有OOP思维... ...等等等。最终的结论就是构建自动化测试对QA的人员提出了很高的要求。在我看来,如果一个QA可以具有了上述能力我想他已经是一个开发人员了(这个要求不切实际)。那可不可以让开发团队来完成这件事情呢?这个当然不行了,理由有N多。这里我只提两条。第一,开发团队的视角往往不是站在用户角度的,因此会极大的提升项目风险。第二,一个自动化测试构建的成功与否?合理的测试计划往往是决定性因素,但是测试计划的制定却不是开发人员所擅长的。(这里没有歧视开发的意思。嘻嘻~~本人也是开发~~~不喜勿喷)。那么,如何构建呢?这个其实也是我们要构建测试框架,分层,使用PageObject模式的原因了,针对上一节我画出的简单结构而言,团队中可以有如下几个角色:
主要是由手工测试人员和懂一些简单编程语法的测试人员组成,他们是PageObject和测试框架的直接使用者(消费者)。使用已经完成的PageObject和测试框架进行测试用例的编写,以及测试计划的制定。以我个人的经验,一般的QA简单的培训一下即可以胜任这样的工作,而他们所完成的却是很大的一部份工作。功能测试人员专注与逻辑的测试,而不易关心技术细节。
这部分人员可以由测试团队中技术(编程技术)较高或是开发人员来担当,要求如下:
因此,这部分同学专注与提供好用的页面类,而不必须关系纷繁复杂的业务逻辑。这部分人也是测试人员的一个职业发展方向(技术型测试)。
这个就需要资深一些的同学来做了,架构师的要求嘛就比较多了。他在团队中主要的任务就是构建测试的基础设施。比如页面导航的管理,提供ORM机制供测试人员使用,日志的输出,测试报告的生成,环境切换,浏览器切换,提供一些炫酷而简单的使用功能,还有就是最一些文档的输出。这样一来,测试架构师也可以关心他所需要完成的技术难题,而不关心业务和具体的页面类的实现。
总结一下,有了这三种分工其实工作在不同的层面上。各自完成所擅长的那一部分。于此同时也提供生了整个测试的可维护性。比如:页面做了修改,只需要PageObject人员去修改自己的封装。而业务逻辑出现了变更(页面不变的话),也只是需要用例编写人员修改一下自己的测试用了而已。
本来是想做一个Demo给大家的。但是... ...过年了... ... 老妈催着回家过年,收拾行李去呀,有机会再补上吧 ... ... 今天就任性一把 哈哈 , 各位小伙伴,新年快乐!
前面讲了分工,现在的你是哪个级别呢?能一篇篇把我的系列文章看到这一篇的,估计多数还是没有构建测试框架能力的小伙伴。所以,后面如果时间允许的话我会写一个关于自动化测试框架构建的系列,记得关注哦~~~
《Selenium For C#》的相关文章:Click here.
说明:
没有超链接的文章写完发表后,我会添加相应的链接,在此先列出目录。
Demo地址:https://github.com/DemoCnblogs/Selenium