从入门到不放弃:多浏览器的自动化测试(1)- 本地测试

本文将作为多浏览器自动化测试的第一篇文章,给读者从头介绍如何进行本地多浏览的自动化测试工作,包括测试的原理、测试框架的选取、测试工程的搭建和实现等。另外“从入门到不放弃”系列将给读者们带来更多从零开始的前端实践案例,诸如前端组件库设计与实施、项目自动化构建等案例,欢迎大家关注本系列的其他文章。

前端之殇

要是你碰到前端工程师朋友,那聊聊浏览器的兼容性准是没错,这和碰到英国朋友就谈天气是一个道理。大部分程序员朋友们一定会捶胸顿足,连连诉苦,不过如果对方一时语塞,或者欲言又止,请拍拍他 / 她肩膀说:

“没事,过两年出了新浏览器又是一条好汉。”

在前端界,浏览器兼容性是让工程师们头疼的问题,对于经验丰富的人来说,很清楚浏览器有哪些坑,但是对于大部分程序员,最可怕的是代码明明在这个浏览器运行得很好,但是到了另一个浏览器中就不能正常运行了。对于这部分的程序员,保障代码能正常运行的方法便是能尽早发现问题,然后将其解决。

bigsec

通常情况下,发现兼容性问题的方法莫过于将程序在各个浏览器中执行一遍,但这是极其浪费人力和时间的,最省力的方法也需要在每次版本的更迭时重复一遍测试工作。对于不同的兼容性要求,测试需要的时间各不相同,若是只支持最新版本的浏览器,那么便测试 3、4 个浏览器即可,但是对于兼容性要求高的程序,有可能要测试 10 个浏览器以上。

对于中小型公司来说,如果没有专职的测试人员,这样的测试耗时是致命的。若进行严格测试,则会拖慢项目进度,倘若敷衍了事,那程序的质量便没法保证。

本文将作为多浏览器自动化测试的第一篇文章,将以项目 A 作为例子,给读者从头介绍如何进行本地多浏览的自动化测试工作,包括测试的原理、测试框架的选取、测试工程的搭建和实现等。在下一篇文章中将介绍如何使用云服务实现更多浏览器的测试工作。另外“从入门到不放弃”系列将给读者们带来更多从零开始的前端实践案例,诸如前端组件库设计与实施、项目自动化构建等案例,欢迎大家关注本系列的其他文章。

小窥测试

测试是一个庞大的主题,包括各种分类的测试,诸如黑盒测试/白盒测试、单元测试 / 集成测试 / 端到端测试等。通常程序员在测试自己的代码的时候用得最多的便是单元测试,但是因为测试也是需要代价,很多人是不喜欢写测试的,甚至是一点都不写。当然今天我们不是要讨伐诸位,而是希望读者能从文中受益,从一个测试小白可以自己动手搭建自己的测试工程。

在多浏览器的自动化测试,我们多半是进行端到端的测试工作,一小部分是大粒度的单元测试。端到端测试测试模拟用户的行为。在 Web 应用程序中,他们会启动服务器,打开浏览器,模拟用户的行为进行点击、输入、提交等动作,断言浏览器中发生了特定的事情或者是得到了期待的结果,从而让我们相信功能可以正常的运行。而单元测试根据代码单元的公共 API 运行它们。这些测试需要创建一个类的实例,使用特定的输入调用它的方法,断言被调用的方法达到了预期的效果。

在下文中我们会看到这两种测试的实践,当然有时候区分度并不大,可能无法明显地区分哪些是端对端测试哪些是单元测试,有时候他们是混合起来的,不过只要记住我们的目标是保证功能可以正常运行就足够了。

在浏览器的测试中,Selenium 可谓是最重要的工具之一。简单来说 Selenium的作用是 "Automate Browsers"——让浏览器可以自动化起来的工具。它提供了统一的接口,让用户可以使用不同的编程语言,调用其接口来模拟用户的操作,例如点击,移动等操作。基本上一切人工操作的行为都可以通过 Selenium 的 API 进行触发操作。我们将 Selenium 看作是人手的代理,帮程序员完成一切用手干的活。

测试的技术方案选择

在进行项目实践前,很重要的一项工作是选择合适的技术栈。好比在前端开发时应该选择 React,Vue 还是 Angular 作为框架一样,前端的测试工作也需要选择一套技术栈。

很多时候大家在制定技术栈时容易走偏,在选择技术框架时不是选择最合适的框架,而是选择最热门的框架。当然一定程度上热门的框架能反应其受欢迎程度,可能是因为其出众的优点,如较高的开发效率、高效的渲染特性或者是活跃的社区。在前端开发中,很容易有这样的感受,就是只要半个月没有关注业界的最新动态,就感觉恍若隔世,新的解决方案层出不穷,让人喘不过气。

就作者本人经验来说,已经过了慌乱的年纪,再也不会盲目地追寻新技术,而转向关注技术背后解决的痛点,就好像 2C 创业者们嘴上老说的用户痛点一样。

bigsec

在介绍本文涉及项目的技术栈之前,需要提醒诸位,此处的技术选择并不一定完全适用于诸位的项目,请各位三思而测。目前市场上有众多的测试框架,测试断言库甚至是全套的测试解决方案。Karma、Jasmine 和 Mocha 是大家熟知的测试框架,而 chai, should.js 是流行的断言库,另外在不同的技术社区还有自成一套的测试技术,比如 React 社区中的 Jest 和 Enzyme 都是受开发者喜爱的测试框架和库,最近一些新的并行测试解决方案也日渐流行,如 AVA 、Intern 。本文中的实践来自于项目 A,在项目测试前期我们分析了测试需求,我们希望整个测试方案能满足一下要求:

  • 支持端到端测试
  • 对接云测试服务方便
  • 本地测试和云测试切换方便
  • 提供封装的浏览器操作接口
  • 测试用例可以快速迁移到其他框架下执行

考量了以上的需求,我们认为 NightWatch.js 是一款非常合适的测试解决方案。当然其他的测试框架也基本能满足需求,但是从方便易用性上考虑,我们最后采用了 NightWatch.js,该方案不仅提供简易封装的浏览器代理操作 API, 还给我们提供了方便便捷的云测试配置(下一篇文章将着重介绍此内容),就凭这两点就已经非常吸引我们了。对于前端测试新手,强烈推荐试用此框架,让你可以迅速完成曾经畏而却步的测试工作。

项目实践

项目 A 的本地测试实践是需要分别在两台电脑上的多浏览器中执行测试,两台电脑分别是 Windows 系统和 Mac 系统,包括了 IE 、Firefox(windows / mac)、Chrome(windows / mac)、Safari 等最新的主流浏览器。两台机子的测试是分别执行的,我们通过 Jenkins 分别定期执行机子上的测试任务,将测试结果通过邮件的方式反馈给开发人员。 Jenkins 是一个持续集成的平台,关于如果使用 Jenkins 请各位自己 Google。

在接下来的文章中,我们将只介绍在一台机子上的工程实践,对于多个机子的测试需要将如下的工程部署到不同的机子,再使用诸如 Jenkins 之类的工具进行定期执行就可以。

开始工作前,我们需要将技术关系了然于心。我们在 Nightwatch 框架下使用 Selenium 中的 driver对浏览器进行操作。不同的浏览器有不同的 Driver,整个技术栈图如图1所示:

bigsec

在图中 Test Runner 即为 Nightwatch,我们使用 Nightwatch 提供封装过的 API 进行 Test Case 的书写。下面我们将从零开始手把手教你如何使用 Nightwatch 启动你的第一个 Test case。

01安装测试所需包

在自己的前端项目中安装 Nightwatch.js,并将其保存在 package.json的 devDependencies 中。

npm install nightwatch --save-dev

02增加 npm script 入口

在 npm scripts 中加入 test 指令入口,该条指令的具体工作是使用 test.conf.js 的配置,执行名为 'A' 、'B' 、'C' 的配置项(若为了直观查看测试的内容,可根据项目的测试浏览器和版本将名字设为 chrome52.0, safari9.0 这样的名字,此处设为 A , B , C 是避免大家误认为是指令是自动根据名字去寻找匹配的浏览器)。更多命令的详解请参照 Nightwatch 文档。

"scripts": {
  ...
  "test": "./node_modules/.bin/nightwatch -c conf/test.conf.js -e A,B"
  ...
}

03配置 Nightwatch

完成指令入口的配置工作,接下来需要完成 test.conf.js 的配置工作。在本地测试中,我们使用 Selenium 对浏览器进行代理操作。配置使用本地 Selenium 操作本机浏览器 Nightwatch 有三个重点:

Selenium 的配置:配置好 Selenium jar 包的路径,该包从 Selenium 的官网上下载,host 和 port 按照下文配置书写。 driver 的配置:cli_args 是 Selenium 参数,在这我们指定了 chromedriver 和 geckodriver 的路径,chromedriver 是用来操作 chrome,geckodriver 用来操作 safari 和 firefox(顾名思义,geckodriver 支持基于 gecko 的浏览器),都可以从网上进行下载。在项目A中,我们将其下载到前端下面的 bin 目录下。 测试目标浏览器的配置:也就是A和B,每一个 Object 都是一个配置项,A是测试Chrome浏览器,B是测试 Safari 浏览器,如果没有指定版本,就使用本地最新版,更多的配置可以参考 Nightwatch 文档,可以指定系统、版本,并可以启动、禁用浏览器的某些特性,如 Cookie。

selenium : {
  "start_process" : true,
  "server_path":"./bin/selenium-server-standalone-3.4.0.jar",
  "host" : "127.0.0.1",
  "port" : 4444,
  "cli_args": {
    "webdriver.chrome.driver": "bin/chromedriver",
    "webdriver.gecko.driver" : "bin/geckodriver"
  }
},
...
test_settings: {
  A: {
    desiredCapabilities: {
      'browserName': 'chrome'
    }
  },
  B: {
    desiredCapabilities: {
      'browserName': 'safari'
    }
  }
}

诸位需要根据自己机子的实际情况进行配置,如果是Windows系统,那么将没有safari浏览器,而使用 IE 浏览器,这样则会需要 IE 浏览器对应的 driver。

04书写测试用例

在各项准备工作完毕后,就只差测试用例了,下面是项目 A 的一个测试用例的片段,用于检测页面上 id 为 testid 的 DOM 中的内容字符,我们期待字符的长度为 32, 如果该字符为 32 个字符,那么测试通过,否则测试失败。

需要注意的是因为此 DOM 是动态插入的,所以在判断其字符前,我们使用 waitForElementVisible 来检查浏览器中 testid 的 DOM 是否已经显示,若在5秒内显示则进行下面的工作,如果没有显示,那么测试也会失败。

module.exports = {
  '@tags': ['unit'],
  'unit testing' : function (browser) {
    browser.url(`http://localhost:3010/test`)
      .waitForElementVisible('#testid', 5000)
      .getText("#testid",function(result){
        this.assert.equal(result.value.length,32);
      });
    browser.end();
  }
};

5. 运行测试

到此为止,我们简单的测试工程已经搭建完毕。现在我们回过头去,执行我们最开始配置的 test 指令,启动测试任务。你需要在命令中执行:

npm test

如果顺利的话,此时你会看到浏览器自动地打开关闭,很快就能从终端上看到如下的测试结果,图 2 展示的是多个测试用例成功的结果,图 3 展示的是测试失败的结果(如遇到无法测试或者其它异常情况请 Google。:D)。

bigsec

bigsec

从测试结果中可以查看测试用例的测试结果,包括测试的浏览器、未通过测试的信息详情等。至此,一个从零开始的本地测试实践教程结束。

本地测试与云测试

因为本地浏览器的类型有限,一般我们更多地使用本地的多浏览器测试来完成功能验证的工作,对于要求更严的兼容性测试,我们将采用云测试的方式。云测试即云服务提供商将向我们提供更多的云主机,每台主机上运行着不同版本的浏览器。通过使用云测试服务,我们就能将测试覆盖到更多类型、版本的浏览器。

在下一篇文章中,我们仍以项目 A 为例子,使用 Nightwatch 框架,在此文章的基础上介绍云测试和云测试工程的搭建。

潘潘,岂安科技软件工程师

同济大学毕业,曾在VMware等多家知名公司实习,3年全栈开发经验,负责岂安科技核心产品初期的前端开发和架构工作。