【基于Puppeteer前端自动化框架】【二】PO模式,断言(如何更简便逻辑的写测试代码)
一、概要
前面介绍了Puppeteer+jest+TypeScript做UI自动化,但是这知识基础的,我们实现自动化要考虑的很多,比如PO模式,比如配置文件,比如断言等等。下面就来一一实现我是怎么用puppeteer 做UI自动化的
二、断言
(一)需要依赖的安装包
依赖包 | 命令 |
---|---|
Jest | npm install jest --save-dev |
@types/jest | npm install @types/jest --save-dev |
expect-puppeteer | npm install expect-puppeteer --save-dev |
@types/expect-puppeteer | npm install @types/expect-puppeteer --save-dev |
我们写过Jest的知道Jest自己就带有断言,但是Jest的断言有时候可能不全满足我们,我们来看看expect-puppeteer的api
链接:expect-puppeteer(api)
(二) 简单介绍下api
- 看api的介绍,expect-puppeteer封装了一些可供我们使用的方法,断言比如toMatch(验证页面是否能匹配到值),toMathcElement((验证页面是否能匹配到元素),这两个还是比较好用的,尤其toMathcElement,具体实战再说
- 其他api,看跟puppeteer api有点不一样,puppeteer一样有click方法那么expect-puppeteer好处是什么呢?这个时候我们就要看源码了这里建议即使有现成的库,我们也要多看看源码
- 查看expect-puppeteer - index.d.ts 发现写了一个ExpectPuppeteer接口,这里里面就有我们所有的api,我们顺便点看任意一个js文件看看,目录(expect-puppeteer - lib - options.js),我们就看看这个文件,看不懂没关系,但是大概我们能猜出,就是控制运行时间的嘛,实际工作中一个case可能会写大量的waitfor()是不是很麻烦,所以,我建议,吧options.js的timeout设置长一点,这样方便更准确的寻找页面元素
- api不多做介绍,照着api文档就会了,比较简单
options.js -> 修改timeout为2s
let defaultOptionsValue = {
timeout: 2000
};
三、PO 模式
问:什么是PO模式?
答:概念自行百度,我就不粘贴了,我想稍微写过一点UI自动化的,应该都会多多少少了解一点,通俗的说,我们把元素,方法,测试case 分开写,这样方便我们去管理,逻辑也不叫清晰,具体下面拿实例来说明
四、实例 (以同程网站为实例)
今天我们来写,从首页进入ly.com,点击机票 - 国内机票 - 验证机票默认弹框
(一)我的脚本目录
-----__tests__
-------ui
--------DomesticTictet
-------- cases
-------- basic.test.ts
-------- element
--------Index
-------- action
-------- Navi.action.help.ts
-------- element
-------- Navigation.help.ts
-----env
------ ly.yaml
-----utils
------ config.js
- 测试用例都在__tests__文件夹中,DomesticTictet,Index 不同模块的文件夹,分别有cases(测试用例存放的文件夹)element(管理页面元素)action(方法)
- env,管理yaml文件的文件夹,所有的yaml文件放在这里
- utils 自己写的工具类,config.js读取yaml文件
(二)element类管理
Navigation.help.ts
import { Page } from 'puppeteer';
import expectPuppeteer = require('expect-puppeteer');
export const Nav_Ticket = '#menuNav li:nth-child(3) b';
export const DomesticTicket = '.submenu-nav .flight-submenu1';
home.help.ts
// 标题名
export const titleContent = '.s-title.dflex span';
// 出发城市
export const start_city_input = '.s-box:nth-child(1) input[value]';
// 到达城市
export const arrive_city_input = '.s-box:nth-child(3) input[value]';
// 出发时间
export const start_data_input = 'input[placeholder="出发日期"]';
// 到达时间
export const return_data_input = 'input[placeholder="返回日期"]';
// 搜索按钮
export const domestic_tictet_search = '.s-button';
// 搜索不到航班信息提示
export const flight_no_data_tip = '.flight-no-data span'
// 存在航班的元素
export const flight_get_data = '.top-flight-info span b'
(三)action方法编写
Navi.action.help.ts
import { Page } from 'puppeteer';
import expectPuppeteer = require('expect-puppeteer');
const navi_element = require('../element/Navigation.help');
export class Navi_Action {
/**
* 点击国内机票
*/
public async hover_home_ticket(page:Page) {
await page.waitForSelector(navi_element.Nav_Ticket);
await page.hover(navi_element.Nav_Ticket);
await page.waitFor(3000);
await expectPuppeteer(page).toClick(navi_element.DomesticTicket);
await page.waitFor(3000);
}
}
(四)yaml配置文件编写
ly.yaml
url:
web: https://www.ly.com/
flighthome: https://www.ly.com/flights/home
# puppeteer lanuch配置
puppeteer:
proxy:
viewport:
width: 1920
height: 1080
(五)测试用例编写
basic.test.ts
import { Page } from 'puppeteer';
import { Navi_Action } from '../../Index/action/Navi.action.help';
const config = require('../../../../utils/config');
const Home_Element = require('../element/home.help');
const time = require('silly-datetime');
const ly = config.readConfig('ly');
describe('domestic ticket page content verification', () => {
let page : Page;
beforeEach( async () => {
page = await browser.newPage();
await page.setViewport(ly.puppeteer.viewport);
await page.goto(ly.url.web,{waitUntil:'networkidle2'});
let navi_action = new Navi_Action();
await navi_action.hover_home_ticket(page);
},30000)
afterEach ( async () => {
await page.close();
})
test('TEST_001:验证跳转链接' , async() => {
const url = await page.url();
await expect(url).toBe(ly.url.flighthome);
},30000);
test('TEST_002:验证标题名' , async() => {
const titleElement = Home_Element.titleContent;
const content = await page.evaluate( (titleElement) => {
return document.querySelector(titleElement).innerHTML;
},titleElement);
await expect(content).toEqual('国内机票');
},30000);
test('TEST_003:验证出发默认城市' , async() => {
const content = await page.$eval(Home_Element.start_city_input,el => el.getAttribute('value'));
await expect(content).toEqual('上海');
},30000);
test('TEST_004:验证到达默认城市' , async() => {
const content = await page.$eval(Home_Element.arrive_city_input,el => el.getAttribute('value'));
await expect(content).toEqual('北京');
},30000);
test('TEST_004:验证时间为当天时间' , async() => {
const current_time = time.format(new Date(),'YYYY-MM-DD');
const start_time_element = Home_Element.start_data_input;
const start_time_content = await page.evaluate( (start_time_element) => {
return document.querySelector(start_time_element).value;
},start_time_element);
await expect(start_time_content).toEqual(current_time);
},30000);
test('TEST_005:验证到达默认值' , async() => {
const return_input = await page.$eval(Home_Element.return_data_input,el => el.getAttribute('placeholder'));
await expect(return_input).toEqual('返回日期');
},30000);
})
结果
【基于Puppeteer前端自动化框架】【二】PO模式,断言(如何更简便逻辑的写测试代码)的更多相关文章
- 【基于PUPPETEER前端自动化框架】【一】TypeScript+Puppeteer+Jest 整合
前提:掌握Jest + Puppeteer 1.Jest环境配置 2.Jest-MATCHERS匹配器 3.Jest-全局变量设置 4.Puppeteer安装 5.Puppeteer元素获取 6.Pu ...
- UI自动化测试框架:PO模式+数据驱动
1. PO 设计模式简介 2. 工程结构说明 3. 工程代码实现 page 包 action 包 business_process 包 util 包 conf 包 test_data 目录 log 目 ...
- 说说UI自动化中的PO模式
PO模式,全称PageObject模式,即页面对象模式.将页面定位与业务操作分离. po模式有以下几个优点: 1.易读性好 2.扩展性高 3.复用性强 4.维护性好 5.代码冗余率低 了解了po模式及 ...
- 3分钟手把手带你搭建基于selenium的自动化框架
1 .什么是seleniumSelenium 是一个基于浏览器的自动化工具,它提供了一种跨平台.跨浏览器的端到端的web自动化解决方案.Selenium主要包括三部分:Selenium IDE.Sel ...
- Selenium基于Python web自动化基础二 -- 免登录、等待及unittest单元测试框架
一.免登录在进行测试的过程中难免会遇到登录的情况,给测试工作添加了工作量,本文仅提供一些思路供参考解决方式:手动请求中添加cookies.火狐的profile文件记录信息实现.人工介入.万能验证码.去 ...
- 自动化框架的两种断言设计(pytest 版)
自动化测试断言失败时,根据不同业务场景,可能需要立即终止或继续执行.这里以 Appium + pytest 为例. 一. 断言失败立即终止 用途一:用例的预期结果是其他用例的前提条件时,assert ...
- net.sz.framework 框架 轻松搭建服务---让你更专注逻辑功能---初探
前言 在之前的文章中,讲解过 threadmodel,socket tcp ,socket http,log,astart ,scripts: 都是分片讲解,从今天开始,将带大家,一窥 net.sz. ...
- ShutIt:一个基于 Python 的 shell 自动化框架
ShutIt是一个易于使用的基于shell的自动化框架.它对基于python的expect库(pexpect)进行了包装.你可以把它看作是“没有痛点的expect”.它可以通过pip进行安装. Hel ...
- 前端Js框架 UI框架汇总 特性 适用范围 选择
身为一个资深后端工程师,面对层出不穷的前端框架,总让人眼花缭乱,做一个综合解析贴,从全局着眼,让我们明白各种前端框架的应用范围,为如何选择前端框架,从不同的维度提供一些线索,做为一个长期优化贴,欢迎指 ...
随机推荐
- vue 路由工程化重构
当项目越来越庞大的时候,路由越来越多,而且遍布的页面也越来越多, 当需要更换地址的时候就无比的繁琐,通过学习了解到可以通过router.js来统一调控 原理: 在路由页面通过name来进行跳转,传入的 ...
- MongoDB Java连接---MongoDB基础用法(四)
MongoDB 连接 标准 URI 连接语法: mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN ...
- 撤销rebase与git原理
git对象 git是面向对象的,对象存储在.git/objects文件夹中.此文件夹中,一个对象就是一个文件,文件名就是对象的id 提交commit的时候,每个文件都是一个数据对象,一个树对象会用来维 ...
- 【总结】springcloud
一.spirngcloud概述 1.springcloud是什么? spring cloud是一个一站式的开发分布式系统的框架,为开发人员提供了快速构建分布式系统中一些常见模式的工具(如:配置管理,服 ...
- Some problems in openMP's parallel for
Overview Somehow I started preparing for the ASC competition.When I'm trying my second demo pi, whic ...
- android 下的 handler Message
研究了下android下的 handler message 实现原理: new handler() 的时候 从ThreadLocal里面 获取当前线程下的 Looper实例下的 MessageQu ...
- cmd中执行mvn help:system报错的解决办法
[ERROR] No plugin found for prefix 'help' in the current project and in the plugin groups [org.apach ...
- SQL Server双机热备之发布、订阅实现实时同步
一.复制的功能概述 SQL Server 复制功能实现了主从库的分离,从而将主库的压力分解掉,主库就主要负责数据的更改等,而主库主要负责查询ji.另外,有了主.从库,则从另一个方面,也了一层安全性,即 ...
- python数据类型互相转换
类型转换 关注公众号"轻松学编程"了解更多. 主要针对几种存储工具:list.tuple.dict.set 特殊之处:dict是用来存储键值对的. 1.list 转换为set l1 ...
- 带货直播源码开发采用MySQL有什么优越性
MySQL是世界上最流行的开源关系数据库,带货直播源码使用MySQL,可实现分钟级别的数据库部署和弹性扩展,不仅经济实惠,而且稳定可靠,易于运维.云数据库 MySQL 提供备份恢复.监控.容灾.快速扩 ...