Puppeteer 实现 web 自动测试

原创 2019年 4月 17日
标签: PUPPETEER
本文发布至今已有5年零249天,可能不再适用,请谨慎对待。

Puppteer 是 Google 推出的自动化工具,包含 Chromium 用于模拟用户侧操作,本文介绍如何 配合 Mocha 和 Chai 使用 Puppteer 开展自动化测试

Puppteer Github 页面 Puppteer API 定义 Puppteer API 定义(中文版)

简介

Puppeteer 是 Chrome Developer Protocol 的上层封装。

将底层复杂的 CDP 协议封装为 Javascript 的 API,所以能实现代码模拟 chrome 操作。

对应用户操作

用 Puppteer 可以模拟用户侧的操作,内置的模型和用户操作 Chrome 的大致对应关系如下:

浏览器动作实例化对象
打开 Chrome 浏览器窗口browser 实例
浏览器 Tabpage 实例

如果使用 async/await 语法编写 Puppeteer 脚本,会大大简化代码,而且更为接近用户的行为,因为我们总是眼睛看到某个事件发生后,再驱动手去做下一个动作。

比如,用户眼睛看到浏览器窗口出现后,手找到地址栏,填入想要浏览的网址。用代码描述这个过程,大致是这样的:

page = await browser.newPage()
await page.goto(url)

配合 Mocha 和 Chai

Mocha 用来组织测试用例,Chai 用来实现断言

新建一个 package.json 如下

{
  "name": "behavior_test",
  "version": "1.0.0",
  "description": "behavior test with puppeteer and mocha",
  "scripts": {
    "test": "mocha"
  },
  "dependencies": {
    "puppeteer": "1.13.0",
    "chai": "*",
    "mocha": "^6.0.2"
  }
}

执行 npm install 后,将 node_modules/.bin 的绝对路径 加入 Path 变量,方便全局执行 mocha,比如

PATH=/path/to/your/puppeteer/project/node_modules/.bin:$PATH

其中的路径仅仅是举例子,具体到每个机器是不一样的

第一个测试代码

在项目目录里新建一个 test 目录,然后在目录下新建 first.js,内容如下:

/**
 * @name first.js
 * @desc 第一个 Puppeteer 脚本
 */

const puppeteer = require('puppeteer')
const expect = require('chai').expect

let browser, page
const url = 'https://www.baidu.com'
const width = 1600
const height = 900

const browserOption = { 
  headless: false, 
  ignoreHTTPSErrors: true,
  slowMo: 100,
  args: [
   `--window-size=${width},${height}`
    ]
  }

const viewport = {
  width: width,
  height: height
}


describe('百度页面测试', function() {
  before(async function () {
    this.timeout(5000);
    browser = await puppeteer.launch(browserOption)
    page = await browser.newPage()
    await page.setViewport(viewport)
  })

  it('打开百度首页', async function () {
    await page.goto(url)
    logo = await page.waitForSelector('#lg')
    expect(logo).to.exist
  }).timeout(5000)

  it('搜索关键字', async function () {
    input = await page.waitForSelector('#kw')
    await input.type('puppeteer')
    btn = await page.waitForSelector('#su')
    await Promise.all([
      btn.click(),
      page.waitForNavigation()
      ])
    container = await page.waitForSelector('#container')
    expect(container).to.exist
  }).timeout(10000)

  after(async function() {
    await browser.close()
  })
})

执行 mocha 后,应该能看到弹出 Chromium 窗口弹出,执行完后自动关闭,同时命令行里打出以下内容:

代码解读

上面代码的逐行解读如下:

describe('百度页面测试', function() {

describe 是测试集,第一个参数,是测试集的名称,将显示在命令行里。

before(async function () {

before 和 after 是 mocha 的一种钩子函数,当某些关键时间点触发执行

async 关键字用来声明该函数使用 async/await 的语法,对这对关键字的理解可以看下一篇 《Promise 概念的理解》

this.timeout(5000);

指定 before 函数的超时时间为5秒钟。由于启动 Chromium 所需时间往往大于默认的2秒钟,如果不指定超时,会导致错误。

browser = await puppeteer.launch(browserOption)

初始化 browser 实例,传递一些参数都是为了方便调试,具体如下:

  • headless: false

关闭 headless 模式,打开 Chrome 实体窗口,方便观察整个过程

  • ignoreHTTPSErrors: true

忽略 https 的报错,避免进入 chrome 的警告页面

  • slowMo: 100

所有动作之间增加 100ms 的暂停间隔,自动化过程是很快的,增加间隔方便观察

  • args: [—window-size=${width},${height}]

规定实体窗口的大小和视口大小一致,默认的窗口只有800x600,比实际可用的视口小

更为详细的参数配置,见这篇文章 《Puppteer 配置浏览器属性》

(Chromium Logo)

puppeteer.launch() 成功实例化后,将对象赋予 browser 变量

page = await browser.newPage()

newPage() 用来生成 page 实例,相当于 Chrome 浏览器中的 一个个 tab

await page.setViewport(viewport)

setViewport() 设置页面视口

it('打开百度首页', async function () {

it() 相当于一个测试用例,第一个参数用来描述用例的期望值,运行测试时会出现在命令行里;第二个参数是一个 async 函数

await page.goto(url)

goto() 将页面导航至指定的地址,页面加载完后,接着执行下一步

logo = await page.waitForSelector('#lg')

waitForSelector() 寻找 id 为 ’lg’ 的元素,如果找到后,赋值给 logo

expect(logo).to.exist

chai 模块的 expect 函数,断言 logo 元素存在

}).timeout(5000)

timeout() 指定该测试用例的超时时间,需要根据网速和网页大小调整

第二个 it() 里的函数都能望文生义,不需要太多解释,这一段需要特别解释:

await Promise.all([
        btn.click(),
        page.waitForNavigation()
      ])

这段代码的场景是处理点击触发页面跳转的通常写法。将点击事件和页面跳转包裹在一起,只有两件事情都完成后,整个Promise 才算完成。接下来的动作(比如寻找元素)都是在新页面里完成。

Promise 概念的理解可以看下一篇 Promise 概念的理解

如果您对本文有疑问或者寻求合作,欢迎 联系邮箱邮箱已到剪贴板

标签: PUPPETEER
给个免费的赞吧~

精彩评论

本站 是个人网站,采用 署名协议 CC-BY-NC 授权。
欢迎转载,请保留原文链接 https://www.lfhacks.com/tech/puppeteer-mocha-chai/ ,且不得用于商业用途。