创建一个工具来快速有效地在Selenium上编写自动测试

基础构件自动化-测试
罗德·约翰逊
图片

我不是测试Web界面的大使,但是这篇文章对已经在该领域有经验的同志很有帮助。

这对于初学者也很有用,因为 我提供了源代码,您可以在其中查看最终产品中与硒的相互作用的组织方式。

我将谈论从无到有的开发经验,如何编写一个用于运行测试的平台以及该平台本身。 我本人相信我的产品被证明是非常有效的,这意味着它将对许多人有用,并且值得考虑。

概念的


测试过程取决于信息系统。

为了记住我的概念,有必要首先了解我关注的系统-这些系统通常在进行回归测试时通常将特定的线性业务流程设置为关键。

因此,像srm这样的系统。 关键业务实体是供应商产品。 进行回归测试的关键考虑因素是业务流程的完整性。
业务流程从供应商在系统中的注册开始,然后继续进行商业投标的创建-进入审阅阶段,该阶段由各种类型的内部用户(每个用户都有唯一的界面)执行,直到退回考虑对供应商的投标决定为止。

事实证明,我们经历了许多不同的界面,并且几乎总是使用不同的界面。 实际上-如果您直接看它-就像看录像带-即 这是一个有始有终的过程,它绝对是线性的-没有分支,编写测试时,我们总是知道预期的结果。 即 我想说的是,已经看过这张图片,我们可以得出结论,使测试多态性不太可能成功。 有鉴于此,在创建用于运行测试的平台时,关键因素是我设定了编写测试的速度。

我为自己设定的概念:

  1. 应尽快创建自动测试。 如果您定性地做到了这一点,那么其他方面,例如可靠性和易用性,就应该自己解决。
  2. 测试必须以声明方式声明,并且必须与代码分开进行。 我什至没有看到其他选择。 这样可以提高书写速度,因为 如果您有现成的解释程序-我们的平台,则无需在以后添加任何内容,也不必再次输入代码-通常,您可以通过IDE忘记完成的平台。 因此测试更易于维护。 以这种形式,他们更容易学习写作,因为 不需要开发技能,只需要了解标记语言即可。 通过这种形式,过程中的所有参与者都可以理解它们。

一开始我决定拒绝的是:

  1. 不要将系统包装在测试框架中。 您可以在没有测试框架的情况下开始执行流程。 “您想发明一辆自行车!”-许多人会说。 我有不同的看法。 流行的二手测试框架的创建主要是为了从内部测试代码,我们将从外部测试系统的外部。 就像我有公路自行车一样,我需要在越野路上下山(粗鲁,但思路已经反映出来)。 总的来说,我们将自己编写框架-使用21点和...(尽管我知道,例如,JUnit 5已经非常适合此类任务)。
  2. 拒绝使用硒包装纸。 实际上,密钥库本身很小。 要了解您需要在完全铲除它的同时使用其功能的5%,这将需要几个小时。 不要再到处寻找一种减少编写代码并习惯便盆的方法。 在现代世界中,这种渴望常常导致荒谬,几乎总是造成灵活性的损害(我的意思是“编写更少代码”的方法,而不是架构框架的情况)。
  3. 无需精美呈现结果。 引入此项目是因为 我没有一次遇到这个问题。 自动测试完成后,我需要知道2件事:总体结果(正/负),以及是否有错误-确切的位置。 也许您仍然需要保留统计信息。 就结果而言,其他所有内容绝对不是必需的。 将美观的设计视为重要的优点,或者在初始阶段花时间在美观的设计上是多余的炫耀。

为了进一步阐明一些细节,我将进一步讨论公司的发展水平以及创建该工具的条件。

由于某些机密情况,我不会透露我工作的公司。

在我们公司中,发展已经存在了很多年,因此所有流程都已经建立了很长时间。 但是,它们远远落后于当前趋势。
所有IT代表都理解,有必要在测试中覆盖代码,在协调未来功能需求时编写用于自动测试的脚本,灵活的技术可以大大节省时间和资源,CI可以简化并简化使用寿命。 但是到目前为止,所有这一切只是慢慢地到达了我们...

软件质量控制服务也是如此-所有测试都是手动执行的,如果您“从上方”看待过程-这是整个开发过程的“瓶颈”。

组装说明


该平台使用JDK 12用Java编写

关键基础结构工具-Selenium Web驱动程序,OJDBC

为了使应用程序正常运行,必须在PC上安装FireFox浏览器52版

应用程序构建组成


图片

对于该应用程序,需要3个文件夹和2个文件:

BuildKit文件夹-包含:

  • jdk12,通过它启动应用程序(JVM);
  • geckodriver.exe-用于Selenium Web Driver与FireFox浏览器一起使用;
  • SprintAutoTest.jar-直接在应用程序实例

Reports文件夹-应用程序完成测试用例后,报告将保存到该文件夹​​。 它还应包含ErrorScreens文件夹,如果发生错误,屏幕快照将保存在该文件夹中

TestSuite文件夹-Web软件包,javascript,一组测试用例(填写此文件夹将单独详细讨论)

•config.properties文件-包含用于连接到Oracle数据库的配置以及WebDriverWait的明确期望值

•starter.bat-用于启动应用程序的文件(如果在最后输入名称TestCase作为参数,则可以自动启动应用程序而无需手动指定TestCase)。

申请简介


可以使用参数(名称TestCase)启动该应用程序,也可以不使用该参数启动该应用程序-在这种情况下,您必须自己在控制台中输入测试用例的名称。

不带参数即可运行的bat文件的一般内容的示例:启动“ AutoTest启动器”%cd%\ BuildKit \ jdk-12 \ bin \ java.exe -Xmx768M -jar --enable-preview%cd%\ BuildKit \ SprintAutoTest.jar

在应用程序的一般启动时,它将查看位于“ \ TestSuite \ TestCase”目录中的xml文件(不查看子文件夹的内容)。 在这种情况下,将进行xml文件到结构正确性的主要验证(也就是说,从xml标记的角度来看,所有标记都是正确的),并采用“ testCaseName”标记中指示的名称,然后提示用户输入可用测试的可能名称之一案件。 如果输入错误,系统将要求您再次输入名称。

收到名称TestCase之后,便会构建内部模型,该内部模型是一堆TestCase(测试脚本)-WebPackage(元素存储),其形式为java对象。 构建模型后,将直接构建TestCase(程序的可执行对象)。 在TestCase的构建阶段,还将进行辅助验证-检查TestCase中所有指定的表单都在关联的WebPackage中,并且在操作中指定的所有元素都在指定页面内的WebPackage中。 (下面将详细介绍TestCase和WebPackage的结构)

构建TestCase之后,脚本将直接运行

脚本操作算法(关键逻辑)


TestCase是动作实体的集合,而动作实体又是事件实体的集合。

测试用例
->列出{动作}
->列出{事件}

当TestCase启动时,Action会顺序启动(每个Action返回一个逻辑结果)

当动作开始时,事件按顺序开始(每个事件返回逻辑结果)

每个事件的结果将被保存。

因此,当所有动作成功完成或动作返回false时,测试即完成。

*崩溃机制

因为 我的被​​测系统是古老的,已经捕获了错误/不是错误的bug,并且某些事件在第一次无法运行时,该平台具有一种可以脱离上述严格的线性测试概念的机制(但是,它是强类型的)。 捕获此类错误时,可以首先重复案例并执行其他操作以能够重复操作

在应用程序的末尾,将生成一个报告,该报告保存在“ \ Reports”目录中。 发生错误时,将截取屏幕截图,并将其保存在“ \ Reports \ ErrorScreens”中

TestSuite填充


因此,测试的描述。 如前所述,需要运行的主要参数是要运行的测试的名称。 此名称存储在目录“ / TestSuite / TestCase”中的xml文件中。 所有测试脚本都存储在此目录中。 可以有任何数量。 测试用例的名称不是从文件名中获取的,而是从文件中的“ testCaseName”标记中获取的。

TestCase设置确切要执行的操作-即 行动。 xml文件中的“ / TestSuite / WebPackage”目录中存储了所有定位符。 即 所有这些都是最好的传统-动作分别存储,Web表单定位器独立存储。

TestCase还将WebPackage名称存储在“ webPackageName”标签中。

总图片已经在那里。 要运行,您必须具有2个xml文件:TestCase和WebPackage。 他们组成一堆。 WebPackage是独立的-标识符是标签“ webPackageName”中的名称。 因此,这是第一条规则-名称TestCase和WebPackage必须唯一。 即 再一次-实际上,我们的测试是一堆TestCase和WepPackage文件,它们通过WebCage名称(在TestCase中指定)连接在一起。 在实践中,我使一个系统自动化,并将所有测试用例编织到一个WebPackage中,在其中我对所有形式的描述都有很多。

逻辑分解的下一层基于诸如页面对象之类的模式。

页面对象
Page Object是自动化中最有用和最常用的体系结构解决方案之一。 此设计模式有助于封装单个页面元素的工作。 实际上,页面对象将被测应用程序的页面建模为对象。

逻辑与实现分离

测试逻辑(要检查什么)与其实现(如何检查)之间有很大的区别。 一个测试方案的示例:“用户输入错误的用户名或密码,按下登录按钮,收到一条错误消息。” 该脚本描述了测试的逻辑,而实现则包括诸如在页面上搜索输入字段,填写它们,检查错误等操作。 并且,例如,如果更改显示错误消息的方法,那么这将不会影响测试脚本,您还需要输入不正确的数据,按登录按钮并检查错误。 但是,这将直接影响测试的实施-必须更改接收和处理错误消息的方法。 通过将测试的逻辑与实现分开,自动测试变得更加灵活,并且通常更易于维护。

*! 不能说这种架构方法已被完全应用。 只需逐页分解测试脚本的描述即可,这有助于更快地编写测试并在所有页面上添加其他自动检查,激发定位符的正确描述(以使它们在不同页面上不相同)并构建测试的``美丽''逻辑结构。 该平台本身是按照“清洁架构”的原则实施的

接下来,我将尽量不详细介绍WebPackage和TestCase的结构。 对于他们,我为WebPackage创建了DTD模式,为TestCase创建了XSD 1.1。

! 重要事项


通过维护DTD和XSD方案,实现了快速测试编写的概念。

直接编写WebPackage和TestCase时,必须使用具有内置实时DTD和XSD验证功能并自动生成标签的xml编辑器,这将使编写自动测试的过程变得非常自动化(所有必需的标签将被自动替换,下拉列表将显示属性值)可能的值,根据事件的类型将生成相应的标签)

当这些方案“拧入” xml文件本身时,如果使用特殊环境,则可以忘记xml文件结构的正确性。 我的选择取决于oXygen XLM Editor。 再一次-如果不使用这样的程序,您将不会理解写入速度的本质。 想法不是很适合这个。 它不处理XSD 1.1“替代”构造,这是TestCase的关键。

网络包装


WebPackaege-描述Web表单元素的xml文件,位于目录“ \ TestSuite \ WebPackage”中。 (可以有任意多个文件。文件名可以是任何东西-仅内容重要)。

DTD(在文档开头插入):
<!DOCTYPE webPackage [ <!ELEMENT webPackage (webPackageName, forms)> <!ELEMENT webPackageName (#PCDATA)> <!ELEMENT forms (form+)> <!ELEMENT form (formName, elements+)> <!ELEMENT formName (#PCDATA)> <!ELEMENT elements (element+)> <!ELEMENT element (name, locator)> <!ATTLIST element type (0|1|2|3|4|5|6|7) #REQUIRED> <!ATTLIST element alwaysVisible (0|1) #IMPLIED> <!ELEMENT name (#PCDATA)> <!ELEMENT locator (#PCDATA)> <!ATTLIST locator type (1|2) #IMPLIED> ]> 


一般而言,它看起来大约
 <webPackage> <webPackageName>_</webPackageName> <forms> <form> <formName>______</formName> <elements> <element type="2" alwaysVisible="1"> <name>_</name> <locator type="2">.//div/form/div/div/form/table/tbody/tr/td[text()=""]/following-sibling::td/input</locator> </element> <element type="2"> <name>__</name> <locator>.//div/form/div/div/form/table/tbody/tr/td[text()=""]/following-sibling::td/input</locator> </element> ....... </elements> </form> ....... </forms> </webPackage> 


如前所述,因此元素不会在堆中-一切都根据Web表单分解

关键实体是
 <element> 

element标签具有2个属性:

  • 类型
  • 始终可见

type属性是必需的,它指定元素的类型。 在平台上,设置字节类型

目前,他特别为自己在平台中实现了以下类型:

•0-没有功能含义,通常是某种题词
•1-按钮(按钮)
•2-输入字段
•3-复选框(复选框)
•4-下拉列表(选择)-尚未实际实施,但保留了位置
•5-对于srm下拉列表:输入名称,等待值出现-根据特定的xpath模板进行选择-适用于我的系统的类型
•6-srm select-用于典型功能,例如搜索等。 -专门为我的系统输入

AlwaysVisible属性-可选-显示元素是否始终存在于表单上,可以在Action的初始/最终验证期间使用(即,在自动模式下,您可以验证当您打开表单时,该元素包含了该元素在关闭时始终存在的所有元素)形式,所有这些元素都消失了)

可能的值:

  • 0-默认情况下(或如果未设置属性)-元素可能不在页面上(不验证)
  • 1-元素始终显示在页面上

可选的type属性通过locator标签实现

可能的值:

  • 1-按ID搜索元素(分别在定位器中仅指定ID)
  • 2-默认情况下(或如果未设置该属性)-在xpath上搜索-建议仅在xpath上使用搜索,因为 该方法结合了其余方法的几乎所有优点,具有通用性

测试用例


TestCase-直接描述测试脚本的xml文件位于“ \ TestSuite \ TestCase”目录中(可以有任意多个文件。文件名可以是任何内容-仅内容重要)。

XSD电路:
 <xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.1"> <xs:element name="testCase"> <xs:complexType> <xs:sequence> <xs:element name="testCaseName" type="xs:string"/> <xs:element name="webPackageName" type="xs:string"/> <xs:element name="actions" type="actionsType"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="actionsType"> <xs:sequence> <xs:element name="action" type="actionType" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <xs:complexType name="actionType"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="orderNumber" type="xs:positiveInteger"/> <xs:element name="runConfiguration" type="runConfigurationType"/> </xs:sequence> </xs:complexType> <xs:complexType name="runConfigurationType"> <xs:sequence> <xs:element name="formName" type="xs:string"/> <xs:element name="repeatsOnError" type="xs:positiveInteger" minOccurs="0"/> <xs:element name="events" type="eventsType"/> <xs:element name="exceptionBlock" type="eventsType" minOccurs="0"/> </xs:sequence> <xs:attribute name="openValidation" use="required"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="closeValidation" use="required"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> <xs:complexType name="eventBaseType"> <xs:sequence> <xs:element name="orderNumber" type="xs:positiveInteger"/> </xs:sequence> <xs:attribute name="type" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="goToURL"/> <xs:enumeration value="checkElementsVisibility"/> <xs:enumeration value="checkElementsInVisibility"/> <xs:enumeration value="fillingFields"/> <xs:enumeration value="clickElement"/> <xs:enumeration value="dbUpdate"/> <xs:enumeration value="wait"/> <xs:enumeration value="scrollDown"/> <xs:enumeration value="userInput"/> <xs:enumeration value="checkInputValues"/> <xs:enumeration value="checkQueryResultWithUtilityValue"/> <xs:enumeration value="checkFieldsPresenceByQueryResult"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="invertResult" use="optional" default="0"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="hasExceptionBlock" use="optional" default="0"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> <xs:complexType name="eventsType"> <xs:sequence> <xs:element name="event" type="eventBaseType" maxOccurs="unbounded"> <xs:alternative test="@type='goToURL'" type="eventGoToURL"/> <xs:alternative test="@type='checkElementsVisibility'" type="eventCheckElementsVisibility"/> <xs:alternative test="@type='checkElementsInVisibility'" type="eventCheckElementsVisibility"/> <xs:alternative test="@type='fillingFields'" type="eventFillingFields"/> <xs:alternative test="@type='checkInputValues'" type="eventFillingFields"/> <xs:alternative test="@type='clickElement'" type="eventClickElement"/> <xs:alternative test="@TYPE='dbUpdate'" type="eventRequest"/> <xs:alternative test="@type='wait'" type="utilityValueInteger"/> <xs:alternative test="@type='scrollDown'" type="eventClickElement"/> <xs:alternative test="@type='userInput'" type="eventClickElement"/> <xs:alternative test="@type='checkQueryResultWithUtilityValue'" type="eventRequestWithValue"/> <xs:alternative test="@type='checkFieldsPresenceByQueryResult'" type="eventRequestWithValue"/> </xs:element> </xs:sequence> </xs:complexType> <!--   EVENTS --> <xs:complexType name="eventGoToURL"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="url" type="xs:anyURI"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventCheckElementsVisibility"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="fields" type="fieldType"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventFillingFields"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="fields" type="fieldTypeWithValue"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventClickElement"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="elementName" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventRequest"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="dbRequest" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="utilityValueInteger"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="utilityValue" type="xs:positiveInteger"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventRequestWithValue"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="dbRequest" type="xs:string"/> <xs:element name="utilityValue" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <!--   EVENTS --> <xs:complexType name="fieldType"> <xs:sequence> <xs:element name="field" maxOccurs="unbounded"> <xs:complexType> <xs:choice> <xs:element name="element" type="xs:string"/> <xs:element name="xpath" type="xs:string"/> </xs:choice> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="fieldTypeWithValue"> <xs:sequence> <xs:element name="field" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="element" type="xs:string"/> <xs:element name="value" type="valueType"/> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="valueType"> <xs:complexContent> <xs:extension base="xs:anyType"> <xs:attribute name="type" use="optional" default="1"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="2"/> <xs:enumeration value="3"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:extension> </xs:complexContent> </xs:complexType> </xs:schema> 


一般视图:
 <!DOCTYPE testCase SYSTEM "./TestSuite/TestCase/entities.dtd" []> <testCase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="testShema.xsd"> <testCaseName>__testCase</testCaseName> <webPackageName>_webPackage__webPackageName</webPackageName> <actions> <action> <name>          </name> <orderNumber>10</orderNumber> <runConfiguration openValidation="1" closeValidation="1"> <formName>______</formName> <events> <event type="goToURL"> <orderNumber>10</orderNumber> <url>&srmURL;</url> </event> ....... </events> </runConfiguration> </action> ....... </actions> </testCase> 


在此行中,您可以看到如何固定xsd方案,以便XML编辑器可以看到它:

 <testCase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="testShema.xsd"> 

在TestCase中,我还使用DTD实体,这些实体分别存储在同一目录中-扩展名为.dtd的文件。 在其中,我存储了几乎所有数据-常量。 我还以启动新测试的方式构建了逻辑,并且在整个测试过程中,创建了新的唯一实体,注册了新的航天器,足以更改此文件中的1位数字。

它的结构非常简单-我举一个例子:
 <?xml version="1.0" encoding="UTF-8"?> <!ENTITY mainNumber '040'> <!ENTITY mail '@mail.ru'> <!ENTITY srmURL 'https://srm-test.ru'> 


如下将这样的常数插入标签值中:

 <url>&srmURL;</url> 

-可以组合。

建议 -编写testCase时,应在文档中指定这些DTD实体,并在一切工作稳定之后将其传输到单独的文件中。 我的xml编辑器对此有困难-它找不到DTD,并且没有考虑XSD,所以我建议这样做

测试案例

testCase-最父标签包含:

  • testCaseName-我们的测试用例的名称,此参数传递给应用程序输入
  • webPackageName - WebPackage的名称,在webPackageName中指定(请参见上面WebPackage的段落)
  • 动作 -动作实体容器

行动

包含:

  • 名称 -名称-建议指定表单名称和主要操作-什么以及为什么
  • orderNumber-序列号-排序操作所必需的参数(由于以下事实而引入:使用某些工具在Java中解析xml时,可以在多线程环境中执行解析,因此顺序可以执行)-指定下一个操作时,可以跳转-即 排序时,仅“或多或少”很重要,因此可以在已描述的内容之间进行操作,而无需更改整个编号
  • runConfiguration-作为操作一部分将发生的实际描述

运行配置

包含:

  • openValidation属性-可选,默认为“ 0”
    • 0-不进行初始表格验证
    • 1-初始表格验证
  • 属性closeValidation-可选,默认为“ 0”
    • 0-不进行最终形式验证
    • 1-最终表格验证
  • formName-将在其中执行操作的表单的名称-WebPackage中的formName值
  • repeatsOnError-可选,指示在失败的情况下应执行多少次重复
  • 事件 -事件实体容器
  • exceptionBlock-可选-发生错误时执行的事件实体的容器

大事记

最小结构单位-此实体显示执行了哪些操作

每个事件都是特殊的,可以具有唯一的标签和属性。

基本类型包含:

  • type属性-指示元素的类型
  • hasExceptionBlock属性是一个可选属性,默认情况下,实现故障机制需要“ 0”-该属性指示我们可以期望此事件发生错误
    • 0-预期没有错误
    • 1-动作可能会出现错误
  • 属性invertResult-可选属性,默认为“ 0”-该属性指示有必要更改事件的结果
    • 0-留下事件的结果
    • 1-将事件结果改为相反

*! 描述预期错误的机制
让我举一个简单的例子,说明我第一次使用它的地方以及如何使它工作。

案例:验证码输入。 目前,我无法自动化,可以说是对机器人的检查失败了-他们没有给我写验证码测试服务(但我更容易建立神经网络来进行识别)))因此,我们可能会在输入时出错。 在这种情况下,我进行了一个控制事件,在该事件中,我检查我们是否没有任何元素-有关错误控制代码的通知,我将hasExceptionBlock属性置于其上。 以前,我要求动作可以有多个重复(5),毕竟我注册了exceptionBlock,在其中我写道,必须按退出按钮进行通知,然后重复动作。

从我的上下文中获取示例。

这是我注册活动的方式:

 <event type="checkElementsInVisibility" hasExceptionBlock="1"> <orderNumber>57</orderNumber> <fields> <field> <element>___</element> </field> </fields> </event> 

这里是事件发生后的exceptionBlock

  <exceptionBlock> <event type="clickElement"> <orderNumber>10</orderNumber> <elementName>_____</elementName> </event> </exceptionBlock> 

是的,一页上的动作可以分解为几个动作。

+谁注意到配置中的2个参数:defaultTimeOutsForWebDriverWait,lowTimeOutsForWebDriverWait。 这就是它们的原因。 因为 我将整个Web驱动程序都放在一个实例中,并且我不想每次都创建一个新的WebDriverWait,所以我有1个快速的驱动程序,并且在出现错误的情况下(或者,如果您只是将hasExceptionBlock =“ 1”,那么用更少的时间就很愚蠢了)明确的等待)-好,您必须同意,请稍等片刻,以确保消息不会经常出现,并每次都创建一个新的WebDriverWait。 好吧,这种情况下不粘手需要拐杖-我决定这样做。

活动类型


在这里,我将提供我的事件的最小集,例如侦察器集,通过它我可以测试系统上的几乎所有内容。

现在,代码可以轻松理解事件是什么以及事件的构建方式。 该代码实质上实现了框架。 我有2类-DataBaseWrapper和SeleniumWrapper。 在这些课程中,描述了与基础架构组件的交互,并且还反映了平台功能。 我将提供实现SeleniumWrapper的接口

 package logic.selenium; import models.ElementWithStringValue; import models.webpackage.Element; import org.openqa.selenium.WebElement; public interface SeleniumService { void initialization(boolean webDriverWait); void nacigateTo(String url); void refreshPage(); boolean checkElementNotPresent(Element element); WebElement findSingleVisibleElement(Element element); WebElement findSingleElementInDOM(Element element); void enterSingleValuesToWebField(ElementWithStringValue element); void click(Element element); String getInputValue(Element element); Object jsReturnsValue(String jsFunction); //Actions actions void doubleClick(Element element); void moveMouseToElement(Element element); void pressKey(CharSequence charSequence); void getScreenShot(String storage); } 

它描述了硒和叠加平台芯片的所有功能-嗯,实际上,主要芯片是“ enterSingleValuesToWebField”方法。 请记住,我们在WebPackage中指定了元素的类型。 因此,在此处填写字段时如何对这种类型做出反应。 我们写1次,忘记了。 首先应自行纠正上述方法。 例如,当前有效的类型5和6仅适用于我的系统。 而且,如果您有诸如过滤器之类的东西,并且需要过滤很多东西,并且很典型(在您的Web应用程序中),但是要使用它,您必须先将鼠标移到该字段上,等待某些字段出现,然后转到某些字段,然后再等待有东西,然后去那里然后输入...愚蠢地规定动作机制1次,在开关结构中对此赋予唯一的类型-不再麻烦-为所有类似的应用程序过滤器获取多态方法。

因此,在“ package logic.testcase.events”包中,有一个抽象类描述了事件的一般动作。 为了创建自己的唯一事件,您需要创建一个新类,并从该抽象类继承,并且该工具包中已经具有dataBaseService和seleniumService,然后确定需要哪些数据以及如何处理它。 这样的东西。 相应地,在创建新事件之后,您需要完成TestCaseActionFactory工厂类,如果可能,还需要完成XSD方案。 好吧,如果添加了新属性-修改模型本身。 实际上,这是非常容易和快速的。

所以,一个侦察兵。

goToURL-通常是第一个动作-单击指定的链接

一个例子:
 <event type="goToURL"> <orderNumber>10</orderNumber> <url>testURL</url> </event> 


fillFields-填充指定的元素

特殊标签:

  • 字段 -实体容器字段
    • 字段 -包含元素标签
      • element-指示webPackage中元素的名称
      • value-要指示的值,具有可选的type属性(如果元素是复选框,则指示值之一:“ check”或“ uncheck”)

  • type属性-指示如何获取值,可选,默认值为“ 1”
    • 1-取指定值
    • 2-在这种情况下,将执行“ \ TestSuite \ JS”目录中指定的JS函数! 重要提示-表示txt文件的名称,不带“ .txt”(到目前为止,我仅以这种形式找到了js函数的应用程序-我在一个地方使用它来生成一个随机客栈,但是可能的应用程序范围很广)
    • 3-在这种情况下,数据库中的查询表示为值,程序将替换该查询的第一个结果

一个例子:
 <event type="fillingFields"> <orderNumber>10</orderNumber> <fields> <field> <element>test</element> <value>test</value> </field> </fields> </event> 


checkElementsVisibility-检查指定的元素是否存在于表单上(即可见,而不仅仅是在DOM中)。 在field属性中,可以指定WebPackage中的元素,也可以直接指定xpath

一个例子:
 <event type="checkElementsVisibility"> <orderNumber>10</orderNumber> <fields> <field> <element>test</element> </field> <field> <xpath>test</xpath> </field> </fields> </event> 


checkElementsInVisibility-checkElementsVisibility相似,反之亦然

clickElement-单击指定的元素

一个例子:
 <event type="clickElement"> <orderNumber>10</orderNumber> <elementName>test</elementName> </event> 


checkInputValues-检查输入的值

一个例子:
 <event type="checkInputValues"> <orderNumber>10</orderNumber> <fields> <field> <element>test</element> <value>test</value> </field> </fields> </event> 


dbUpdate-在数据库中执行更新( oXygen对dbUpdate类型的1个事件做出奇怪的反应-我不知道该怎么办,也不知道为什么

一个例子:
 <event type="dbUpdate"> <orderNumber>10</orderNumber> <dbRequest>update - </dbRequest> </event> 


CheckQueryResultWithUtilityValue-使用数据库中的值检查用户输入的值

一个例子:
 <event type="checkQueryResultWithUtilityValue"> <orderNumber>10</orderNumber> <dbRequest>select ...</dbRequest> <utilityValue>test</utilityValue> </event> 


checkFieldsPresenceByQueryResult-通过xpath按模式检查表单上元素的存在。 如果未指定所需的模式,则将根据模式.//* [text()[包含(normalize-space(。),“ $”)]]]进行搜索,其中从数据库中取一个值代替“ $”。 描述自己的模式时,应在要放置数据库值的位置指示“ $”。 在我的系统中,存在所谓的网格,其中通常从某种角度形成值。 此事件可测试此类网格

一个例子:
 <event type="checkFieldsPresenceByQueryResult"> <orderNumber>10</orderNumber> <dbRequest>test</dbRequest> <utilityValue></utilityValue> </event> 


等待 -一切都很简单-等待指定的毫秒数。 不幸的是,即使这被认为是拐杖,我也可以肯定地说-有时候没有它是不可能的

一个例子:
 <event type="wait"> <orderNumber>10</orderNumber> <utilityValue>1000</utilityValue> </event> 


scrollDown-从指定元素向下滚动。 方法是:单击指定的元素,然后按“ PgDn”键。 在我必须向下滚动的情况下,它可以正常工作:

一个例子:
 <event type="scrollDown"> <orderNumber>10</orderNumber> <elementName>test</elementName> </event> 


userInput-在指定的元素中输入一个值。 我自动化中唯一的半自动设备,仅用于验证码。 显示输入值的元素。 该值在弹出对话框中输入。

一个例子:
 <event type="userInput"> <orderNumber>10</orderNumber> <elementName>capch_input</elementName> </event> 


关于代码


因此,我试图按照Bob叔叔的“清洁架构”的原则构建平台。

包装方式:

应用程序-初始化,启动+配置和报告(不要为我的Report类而责备-这就是使其尽可能快,然后尽可能快)的原因

逻辑-Selenium和DB的关键逻辑+服务。 有事件。

模型-XML中的POJO和所有辅助对象类

utils-硒和数据库的单例

要运行该代码,您需要下载jdk 12并在各处指定,以便其芯片打开。 在Idea中,这是通过Project Structure-> Modules and Project完成的。 也不要忘记Maven赛跑者。当您从bat文件开始时,添加--enable-preview。一个例子是。

好了,对于一切开始,通过JDK,您将需要下载ojdbc驱动程序并将dzharnik拖放到“ SprintAutoTest \ src \ lib”目录中。我没有提供,因为现在,那里的Oracle一切都很认真-要下载必须进行注册,但是我敢肯定每个人都会以某种方式应付(嗯,请确保已创建所有文件夹,否则将不保存报告)

总结


因此,我们有一个测试启动器,可以为之快速地编写测试。在一周的工作中,我能够自动执行1.5个小时的手动工作,该工作由机器人在5到6分钟内完成。这些大约是3700行的串联测试用例和830个描述的元素(超过4800行)。这个数字很粗略,因此无法衡量,但是从事自动化工作的人应该理解这是一个很高的数字,尤其是对于机器人不友好的系统。同时,我测试了所有内容-业务逻辑,以及对填充属性的正确性进行了一些否定性测试,此外,我检查了我不懒惰的每个网络表单,并描述了所有功能和关键元素,它们是独立需要的我信不信(离题-我主要在编写测试时才使用closeValidation。当它很稳定并且很明显定位器没有相交时,我对所有操作都将其关闭,以便过程进行得更快。

乍一看,似乎有很多xml行,但实际上它们是半自动生成的,并且只能在直接输入的参数中产生错误(因为实际上我们有2个级别的验证-第一个是方案的xml,第二个是验证的xml在TestCase开始时检查是否存在指定的表单和元素)。

缺点-测试没有明确的界限。因此,它们不见了,您可以怪我这只是一个宏启动器,而不是测试。我这样说:

在平台中,测试从概念上从几个角度分为几个抽象层次:

  • + — « » + — ( – .. -)
  • ( action — event c + )
  • , , – -. , – . , . , – ,
  • 页面上的每个操作都是单元测试(返回单个结果为true或false)

如果将我的方法与某些方法进行比较,那么流行的Cucumber和BDD的缺点对我来说就更为重要(正是当我们测试像我的系统那样的时候):

  • 测试不稳定的Web应用程序时,我们不能保证测试的性能。 就我而言,对于大多数测试,我们不能保证它们的执行,并且将落在“何时”(如果我用一组测试来描述测试),这在我看来通常是不可接受的。
  • 到处都有这个已经登录的巨大例子。是的,在我的所有实践中,正是在登录过程中,从未发生过错误(尽管当然应该涵盖错误,这是肯定的)。这个例子很好,但是对于其余的测试,您需要从Given和When中雕刻出无休止的拐杖-在我给出的真实测试系统中,描述中间条件需要99%的时间来描述,而在我们进行此测试本身时,这会带来很多麻烦,一点点精髓以及进入代码。

从我想做的事情,想一想,但我的手还没有伸手:

  • 一次运行而不是一项,而是依次运行多项测试
  • 我认为,抽水实体,以便它们可以根据新测试的事实生成值,并在执行期间将其保存
  • 进行集中版本控制。一个智能模块不仅可以理解git,还可以指示运行哪个版本的测试,或者一次又一次地智能模块,该模块可以了解哪个版本相关
  • 就像我说的那样,要使用新值开始新的测试,我需要更改1位数字,并使其自动化,为此创建一个智能模块
  • 尽管将所有定位器存储在一个地方并不会让我感到困扰,但仍然以一种很好的方式,我必须为存储它们放置一个更加用户友好的结构
  • 没有涉足硒服务器。我认为这值得考虑,还有可能进一步适应CI,Team City等。

好,仅此而已。我附上对github:源代码的引用

对于建设性的批评,我将感到非常高兴,我希望这个项目真的有用。

Source: https://habr.com/ru/post/zh-CN450726/


All Articles