• 前端自动化测试入门教程


    🪴 背景

    前端的自动化测试主要可以分为以下四种:

    • 单元测试(Unit Test):对一个函数/组件进行测试,一般用于公共函数/公共组件的测试维护。常用框架有 Jest、Jasmine、Mocha等;

    • 集成测试(Integration Test):对多个模块作为一个整体进行测试,一般用于耦合度较高的函数/组件、经过二次封装的函数/组件、多个函数/组件组合而成的函数/组件等。常用的框架有ReactTestUtils、Enzyme、Vue-Test-Utils 、React-Testing-Library等;

    • UI测试:是对界面样式和交互的测试。

    • 端到端测试(end to end,简称e2e):模拟用户操作的黑盒测试。设定一系列操作,测试系统是否能够按照我们设置的步骤正确执行,可以完整测试整个功能的运行。常用的框架有 Puppeteer、Cypress、Playwright(微软出品,配合vscode插件使用)、Selenium 、cucumber、TestCafe等。

    前端开发一般也就单元测试用到比较多,像e2e这种测试几乎不会用到。

    确实,因为e2e测试也有一定的开发成本,再好的东西也得适用不是。

    那么什么情况下适合引入自动化测试呢?例如以下三个场景:

    • 公共类库、公共组件的开发维护;
    • 中长期项目的迭代/重构;
    • 引用了不可控的第三方依赖;

    举个例子,项目中有一个非常重要的下单页面,经过长期的功能迭代重构,核心逻辑可能没变,但是新增了很多新功能,也修改了部分旧功能,已经是一个非常重要的屎山页面。

    这时候要你接手这个页面做一些调整,你能保证捋清并且不破坏掉以前的代码逻辑逻辑吗?我敢说你不敢保证,修改点代码生怕造成“事故”,全公司通报。

    这时候自动化测试的重要性就体现了,如果在开始就设计好一套核心逻辑的自动化测试脚本,那么在之后迭代的过程中不仅可以通过跑测试脚本缕清逻辑,并且还能在修改代码后用脚本验证自己是否破坏了核心的逻辑。

    本文和大家讲解一下e2e自动化测试框架 – puppeteer的基础使用教程。

    🌻 教程

    1. Puppeteer是什么?

    Puppeteer 是一个 Node.js 库,使用它可以运行一个无头的谷歌浏览器(即不显示图形用户界面的浏览器)。然后我们就可以在运行的浏览器中启动我们的页面,并且通过 Puppeteer 提供的 API 操作页面了。

    Puppeteer默认是以 headless 无头模式运行,在写测试脚本的时候,为了方便看到效果,可以配置关闭无头模式,即可正常打开浏览器看到页面自动操作的效果。

    2. Puppeteer能做什么?

    基本上我们平常在浏览器中进行的操作都可以用 Puppeteer 模拟操作,例如:

    • 操作dom,执行dom的各种点击,移动等事件;
    • 模拟键盘输入,鼠标操作。触摸移动等一系列操作;
    • 捕获网站的timeline trace,分析网站性能;
    • 网页截图,生成 PDF;
    • 抓取 SPA(单页应用)并生成预渲染内容(即“SSR”(服务器端渲染))
      等…

    3. Puppeteer怎么上手?

    (1) 首先安装 Puppeteer

    安装 Puppeteer 时会安装一个内置的 Chromium 浏览器,国内用 npm 安装会报错失败,装不上 Chromium,可以改用 yarn 或者 cnpm 安装。

    puppeteer还提供了一个轻量级的包—puppeteer-core,这个包不需要下载 Chromium,而是直接启动现有浏览器或连接到远程安装。

    yarn add puppeteer
    //或者
    cnpm i puppeteer
    
    • 1
    • 2
    • 3

    (2) 上手

    先 require 引入 puppeteer

    const puppeteer = require('puppeteer');
    
    • 1

    然后开始使用 puppeteer 启动一个浏览器并操作页面。首先需要创建一个 Browser 实例,打开页面,然后使用就可以使用 Puppeteer 的 API 进行你想要的操作了。

    const puppeteer = require('puppeteer');
    
    (async () => {  
        //首先创建一个 Browser 实例
        const browser = await puppeteer.launch();  
        //然后创建一个 tab页的 Page 实例
        const page = await browser.newPage();  
        //然后可以打开我们的网页了
        await page.goto('https://example.com');  
    })();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到我们用了 async / await 语法,是的,在 puppeteer 中,几乎所有操作都是异步的,所以一般使用时需要频繁的用到 async / await ,所以使用前要检查 Node 版本大于 v7.6.0。

    如果你需要手动下载 Chromium,还可以配置 Chromium 或 Chrome 可执行文件的位置。

    const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'});
    
    • 1

    正如我们上面所说,怎么关闭 puppeteer 的无头模式呢?只需要在实例化时配置即可。

      //关闭无头模式
      const browser = await puppeteer.launch({headless: false}); 
    
    • 1
    • 2

    配置无头模式之后,我们就可以看到puppeteer会调起一个可视的 Chromium 或 Chrome 浏览器了,这样方便我们直观的看到效果。但是由于puppeteer的操作一步接一步,速度非常快,可能看不清,所以可以使用slowMo参数配置延迟每步操作的时间,如下:

      //关闭无头模式
      const browser = await puppeteer.launch({
        headless: false,
        slowMo: 1000
      }); 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4. Puppeteer的 API

    puppeteer 常用的实例有以下几种:

    • Brower(浏览器实例):Puppeteer通过 launch
      connect 方法连接到 Chromium 时创建一个浏览器实例;可以执行浏览器断开连接、重连、关闭浏览器、打开tab页、获取useragent代理信息等操作;
    • Page(tab页实例):Brower实例通过newPage方法打开一个tab页。关于页面内容的操作一般都会用到这个实例,例如截图、操作dom、跳转tab页、关闭tab页、延迟操作。凡是和页面元素相关的基本都是用它。
    • Keyboard(键盘实例):模拟键盘操作
    • Mouse(鼠标实例):模拟鼠标操作
    • Touchscreen(触屏操作):模拟触屏操作
    • Frame窗口实例

    我这里就简单说明几个常用操作,详细的 API 使用可以查看官网文档

    (1)获取元素 page.$()、page.$$()

    page.$()相当于 document.querySelector,返回匹配的元素节点的第一个。比如获取按钮,然后执行点击事件:

    let submitBtn = await page.$("#submit");
    submitBtn.click()
    
    • 1
    • 2

    page.$$(selector) 相当于 document.querySelectorAll,返回所有匹配的元素节点。

    let inputArr = await page.$$('input')
    
    • 1

    (2)获取元素属性 page.$eval()、page.$$eval()
    获取元素属性不能像平常写js那样,获取到元素之后,用dom.value获取它的值或者获取其他属性。可以使用 page. e v a l ( ) 或 p a g e . eval() 或 page. eval()page.$eval() 获取元素属性,区别和上面获取元素雷同。

    $eval()是对单个元素进行操作。

    $$eval()是对匹配到的所有元素进行操作。

    //获取输入框的值
    const value = await page.$eval('input[name=search]', input => input.value)
    //获取输入框的个数
    const num = await page.$$eval('input', input => input.length)
    
    • 1
    • 2
    • 3
    • 4

    (3)脚本注入 page.evaluate()

    如果要执行某段自定义的js代码,可以用page.evaluate()方法。
    例如按钮点击你也可以这么写:

    await page.evaluate(() => document.querySelector("#submit").click() )
    
    • 1

    (4) 执行等待
    有时候可能需要延迟代码执行,例如修改了元素的可见性,需要等元素显示出来再执行操作,就可以用page.waitForSelector延迟代码执行。常用的延迟执行的api有:

    • page.waitForSelector():等待选择器解析的页面元素出现在页面中;
    • page.waitForNavigation():等待页面跳转后;
    • page.waitForXPath():等待 xpath 解析的页面元素出现在页面中;
    • page.waitForFunction():等待放到页面上下文执行的方法返回真值;
    • page.waitForRequest():等待页面上发起的请求满足判断条件并返回真值;
    • page.waitForResponse():等待页面上接收的请求响应满足判断条件并返回真值;
    • page.waitFor():可充当 page.waitForXPath、page.waitForSelector 、page.waitForFunction 和延时效果用。

    (5)page实例分为三个管理模块

    • _frameManager : 管理页面相关行为,例如页面跳转(goto),等待加载(waitFor), 元素选择与处理(evaluate)等
    • _networkManager : 管理网络相关行为,例如请求拦截(setRequestInterception):离线模式(setOfflineMode)等
    • _emulationManager - 管理模拟行为,例如修改浏览器的UserAgent代理信息,修改视窗大小等

    👣 实操

    我这里写一个简单的小案例,方便大家理解。

    打开掘金搜索到我的主页,进入个人主页并截图

    创建 check.js,具体的操作步骤看下面代码里的注释:

    const puppeteer = require('puppeteer');
    
    (async () => {
    
      //创建 brower 实例,关闭无头模式,方便查看效果,同时可以设置 slowMo ,放慢自动化操作。
      const browser = await puppeteer.launch({
        headless: false,
        slowMo: 0
      });
      
      //创建 page 实例
      const page = await browser.newPage();
      
      //设置视口宽高
      const viewWidth = 1000
      const viewHeight = 800
      await page.setViewport({
        width: viewWidth,
        height: viewHeight,
      });
      
      //跳转掘金首页
      await page.goto('https://juejin.cn/');
      
      //使用 type 方法向掘金搜索框输入 “前端阿彬”
      await page.type('.search-input','前端阿彬',{
        delay: 300
      })
    
      //使用 keyboard 类的 press 方法模拟键盘按下 "Enter" 键
      await page.keyboard.press('Enter');
    
      //这里一定要用 waitForSelector 方法等搜索结果显示出来再继续操作
      await page.waitForSelector('.nav-item.route-active')
    
      //点击搜索结果的 “用户” tab
      await page.evaluate(() => document.querySelectorAll(".nav-item.route-active>a")[4].click() )
    
      //等用户列表显示出来再继续操作
      await page.waitForSelector('.main-list .item')
    
      //获取第一个item,也就是我的个人主页的a链接地址
      const href = await page.$$eval('.main-list .item a',link => link[0].href)
    
      //获取到个人主页的href,然后用goto跳转
      await page.goto(href);
    
      //等待页面跳转完成再继续操作
      page.waitForNavigation()
       
      //调用 screenshot 方法进行截图,并裁减可视区域内的部分
      await page.screenshot({
        path: '个人主页.png',
        clip: {
          x: 0,
          y: 0,
          width: viewWidth,
          height: viewHeight,
        }
      });
     
      //关闭浏览器
      await browser.close();
    })();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    命令行执行 node check.js 执行我们的测试脚本,效果如下:

    tutieshi_640x668_9s.gif

    截图如下:

    个人主页.png

    最后

    puppeteer的功能非常强大,基本上平常在浏览器里能手动完成的操作都可以用它模拟。而测试脚本逻辑无非就是调用它的 API,实际逻辑其实还是靠js,所以上手并不难。

  • 相关阅读:
    传统网站与数字化网站的对比
    JAVA 反序列化之 Apache Commons Collections 反序列化漏洞分析
    【2-3个月左右录用】物联网、无线通信类、人工智能、传感器、人机交互等领域必投快刊,进展顺利,12月截稿
    猿创征文|Linux环境Redis部署及最佳实践
    [思维]Theramore 2022杭电多校第8场 1001
    C++ string 类实现
    20230921研发面经总结
    PG::FunboxEasyEnum
    如何安全传输存储用户密码?(程序员必备)
    B_QuRT_User_Guide(29)
  • 原文地址:https://blog.csdn.net/qq_38974163/article/details/133649430