Puppeteer之提高UI层测试可读性

代码被阅读的时间远大于编写的时间,易于阅读、易于维护的代码可以有效降低自动化脚本维护成本。此次课程将学习如何提高UI层自动化脚本的可读性和可维护性。另外,测试代码也需要持续优化,优化的前提是定义期望达到的度量指标并进行持续优化。其中,自动化测试成功率、反馈时间是2个重要的衡量指标。此章节还会介绍如何通过测试报告获取自动化测试成功率、反馈时间。为了完成此次课程目标,拆分了2个task。

  • 如何提高脚本的可读性和可维护性
  • 如何获取自动化测试成功率和反馈时间

接下来,就开始第一个task吧。

如何提高脚本的可读性和可维护性

当你阅读一个具体的测试用例时,在可读性方面期望的结果是团队开发或者测试人员阅读测试用例后,能明确知道用例验证的业务场景、如何完成用例执行以及用例结果验收标准。对于UI层自动化案例,标准也是一样,期望团队成员看到Case层的代码时能知道该自动化案例验证的具体业务场景、执行过程、结果验证标准。那哪些原则有助于我们达到这样的目标呢?下面列举了提高代码可读性和可维护性的一些指导原则。

  • 代码结构分类清晰,前面讲解过完整的自动化测试分三块,数据管理,配置信息管理,编写执行脚本并完成校验。这三部分需要分类管理,职责清晰,不要出现交叉。
  • 代码分层清晰,常用的分层有3层结构:基础层、Page层、Case层、或者4层结构:基础层、Page层、业务逻辑层、Case层。
  • 函数粒度适度,前面讲解过可以通过Page Object设计模式管理UI层测试代码,除采用此模式外,Page内部的函数粒度也要适度,即能达到复用的效果,也要防止函数过小。
  • 有意义的函数名称,在编写自动化脚本过程中要做到看其名知其意。

上面只是列举了指导原则,接下来我们就针对每个原则以具体的例子进行介绍。如下图所示,是一个代码结构分类的例子。

上面的例子中代码结构分类中第一层包含config、feature、testdata三个目录。对应着配置信息管理;测试场景执行、校验;测试数据管理。

  • config目录负责配置信息管理,该目录下包含静态配置文件以及获取配置信息的方法。

  • feature目录中包含case和page目录,page目录下又按业务场景存放该业务场景相关的page,例如假设blogManage是一个比较大的业务场景,与该业务场景相关的Page都存放在此目录,Page的文件命名要有意义,尽量做到看到文件名字就知道是web应用的哪个页面。另外,这里还设置了commonPage,如果有些页面是多个业务场景共用的,那么可以放到commonPage中。

  • testData目录中包含dataFile和dataManage目录,dataFile目录中存放静态测试数据文件,dataManage中存放读写测试数据的脚本。如果系统比较复杂,准备数据场景多样化,那么dataManage下面还可以按业务场景建立目录,分类管理不同业务场景的数据管理脚本。

接着,我们看看代码的分层管理,如下图所示,显示了3层结构例子,其中基础层的脚本,部分是直接供Case层调用,例如数据的准备和清理,肯定是在具体的Case层进行调用。部分供Page层调用,例如获取配置信息脚本,根据获取的配置信息中应用的baseUrl,LoginPage脚本goto到不同的web页面。

上面的三层结构中,没有提到使用BDD框架,为什么这里没有建议再套一层BDD框架呢?我们先了解下BDD框架的作用,BDD顾名思义是行为驱动开发,它鼓励软件项目中的开发者、测试和非技术人员协助完成项目的自动化测试。即测试人员或者业务人员用DSL语言描述业务场景,下面真正的步骤执行脚本由开发人员或者测试人员完成。最上层用DSL语言描述业务场景最贴近真正的业务场景,也方便团队中所有角色(尤其是无代码能力的角色)能看懂每个用例验证的具体业务场景,让自动化测试代码成为团队的活文档。

我们看一个套用BDD框架cucumber完成的一个自动化案例。

Feature: Simple maths
  In order to do maths
  As a developer
  I want to increment variables   
  //这里是对整个feature要验证的功能描述,类似jest框架中describe("")中添加的描述信息

  Scenario: easy maths
  //对每个具体的场景的描述,类似jest框架中it("")中添加的描述信息

    Given a variable set to 1
    When I increment the variable by 1
    Then the variable should contain 2

  // 测试三步骤,前置条件,执行步骤,期望结果。given-when-then后面接的是具体的step   

  Scenario Outline: much more complex stuff
    Given a variable set to <var>
    When I increment the variable by <increment>
    Then the variable should contain <result>

    Examples:
      | var | increment | result |
      | 100 |         5 |    105 |
      |  99 |      1234 |   1333 |
      |  12 |         5 |     17 |

  // 除上面直接在Scenario层传递测试数据的方式,还可以用Data-Driven,这样相同的测试,可以传递不同的数据组合进行测试   

上面是Feature层代码,也就是用DSL描述的业务场景代码。为了让上面的脚本运行起来,还需要封装step,也就是given-when-then后面真正要执行的步骤。step脚本如下所示。

const { Given, When, Then } = require("cucumber");
const { expect } = require("chai");

Given("a variable set to {int}", function(number) {
  this.setTo(number);
});
//feature文件中当在输入“Given a variable set to 1”,实际执行的是step文件中封装的这个方法

When("I increment the variable by {int}", function(number) {
  this.incrementBy(number);
});
//feature文件中当在输入“When I increment the variable by 1”,实际执行的是step文件中封装的这个方法

Then("the variable should contain {int}", function(number) {
  expect(this.variable).to.eql(number);
});
//feature文件中当在输入“Then the variable should contain 2”,实际执行的是step文件中封装的这个方法

 可以看到使用BDD框架,最大的好处是feature层清晰描述了测试场景。在使用puppeteer时因为已经和jest框架集成,使用jest框架可以在describe或者it中添加对用例的描述信息,所以这里再加一层BDD框架,带来的收益有限。另外,使用BDD框架时可以描述用例的三步骤即given-when-then,在使用puppeteer时,如果方法名称足够表意,看到方法名称能知道方法背后执行的具体业务操作步骤,即便没有given-when-then的描述信息,代码可读性也是很好的。另外,如果是特别复杂的场景,也可以通过添加注释的方式,加上given-when-then的描述信息。故使用puppeteer时不建议再加一层BDD框架,毕竟增加任何内容都是有成本的。

对于一些复杂系统或者大公司期望各个团队都采用比较统一的风格,便于各个系统间复用已经封装好方法,可能会在三层结构的基础上再封装一层,叫业务逻辑层。也就是Case层不直接调用Page中封装的方法,把同一个Page中封装的多个方法或者不同Page中封装的方法组合到一起,完成一个具体的业务场景操作,Case层只调用业务逻辑层封装的方法。增加一层业务逻辑层只是让代码清晰的一种思路,是否添加该层需要看具体项目情况。记住,真正的目标是让代码分类清晰、易于阅读、易于复用,一切有利于此目标的优化都可以考虑,同时要兼顾收益和成本。

下面是一个增加业务逻辑层的例子,左边是要完成的一个测试场景,右边是个伪代码,其中登陆流程和查找图书就是两个业务逻辑层的封装,当任何Case层脚本需要完成登陆或者查找图书操作时,就可以调用LoginFlow和SearchBookFlow。

接下来,我们再看看如何把控Page文件中函数粒度,如果一个函数很大,也就是一个函数中包含多个页面操作步骤,那么可能不利于复用。例如一个函数中包含10个页面操作步骤,但是,可能只有前面5个步骤是多个测试案例都会共用的。如果一个函数粒度很小,比如一个页面操作步骤封装一个方法,那么可能这个方法名不代表具体的业务操作含义,且还会导致Case层调用时不够简洁。这里没有统一的标准来规定函数的具体大小应该是多少。但在封装这些函数时,可以有两个指导原则。

  • 封装函数原则一:如果函数影响复用,导致很多重复代码,那么可能需要进行拆分,减少重复代码。

  • 封装函数原则二:函数参数过多不利于维护,建议一个封装的函数参数在3个左右。

  • 封装函数原则三:在上面两个原则基础上,能组合成一个完整场景的操作放到一个函数中。例如登陆操作,里面包含打开登陆页面,输入用户名、密码,点击登陆按钮,这三个操作完成一个完整的业务操作场景,建议放到一个函数中。

  • 封装函数原则四:如果原则三和原则一二有冲突,那么可以在Page文件中封装一些私有方法,做到方法足够小,不阻碍复用,然后把多个私有方法进行组合完成一个完整的业务场景对外提供。

指导原则最后一点是命名有意义的方法名称,这个就和每个系统业务相关了,对于这一点没有什么技巧,如果是多人协助维护自动化脚本,如果能保证所有参与的人员能看其名知其意即可。

需要注意一点,上面提到的只是指导原则,理论上没有一套放之四海皆准的标准。比如Page Object设计模式建议按页管理对该页面元素操作的所有脚本。如果某些页面很复杂,例如天猫首页,把操作该页页面元素的所有脚本都放到一个js文件中,那么这个js文件会非常大,导致可读性、可维护行降低。遇到这样的复杂页面,实际可以按页面所涵盖的业务场景进行拆分,这样来控制每个Page.js文件大小。总的来说,每个系统情况不同,持续优化是唯一的解决办法,当你觉得某些脚本的分类不够清晰导致可读性差时,就应该考虑是否有更好的分类方式。

上面讲解了提高UI层测试脚本的可读性指导原则,接下来看看如何通过测试报告获取自动化测试的成功率和反馈时间指标。

如何获取自动化测试成功率和反馈时间

可以安装“jest-html-reporter”生成测试报告。执行“npm install jest-html-reporter”按此依赖,然后在jest.config.js文件中添加测试报告的配置。

module.exports= {
    preset: 'jest-puppeteer',
    setupFilesAfterEnv: ['expect-puppeteer','./jest.setup.js'],
    testRunner: 'jest-circus/runner',
    reporters: [
        "default",
        ["./node_modules/jest-html-reporter", {
            "pageTitle": "Test Report"
        }]
    ]
  //以上是关于测试报告的设置,如果不设置测试报告存放路径,默认存放在代码根目录下,文件名称是“test-report.html”  
};

 package中配置testSuite,"testSuite": "jest -i '/scenario/basic/'", 执行“npm run testSuite”运行basic目录下所有.test.js文件,生成测试报告如下所示

可以看到通过测试报告可以知道整个testSuite下测试用例的成功率和运行时间。

使用“jest-html-reporter”的不足之处是没有提供测试报告合并功能,实际项目中,为了并发运行缩短反馈时间,需要把项目的自动化案例拆分成多个testSuite,当在CICD平台运行时,不同的testSuite在不同的构建机上运行,因为无测试报告合并功能,后面运行的案例会覆盖前面案例的运行结果。折中的办法是需要获取某个构建机上testSuite的测试报告时才添加测试报告配置。例如某个构建机上的testSuite成功率比较低,那么可能开启测试报告功能,只运行该构建机上的测试报告,并通过测试报告得到经常失败的案例进行分析优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

taoli-qiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值