【基於Puppeteer前端自動化框架】【二】PO模式,斷言(如何更簡便邏輯的寫測試代碼)

1、概要

前面介紹了Puppeteer+jest+TypeScript作UI自動化,可是這知識基礎的,咱們實現自動化要考慮的不少,好比PO模式,好比配置文件,好比斷言等等。下面就來一一實現我是怎麼用puppeteer 作UI自動化的web

2、斷言

(一)須要依賴的安裝包

依賴包 命令
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==npm

連接:expect-puppeteer(api)

(二) 簡單介紹下api

  1. 看api的介紹,expect-puppeteer封裝了一些可供咱們使用的方法,斷言好比toMatch(驗證頁面是否能匹配到值),toMathcElement((驗證頁面是否能匹配到元素),這兩個仍是比較好用的,尤爲toMathcElement,具體實戰再說
  2. 其餘api,看跟puppeteer api有點不同,puppeteer同樣有click方法那麼expect-puppeteer好處是什麼呢?這個時候咱們就要看源碼了==這裏建議即便有現成的庫,咱們也要多看看源碼==
  3. 查看expect-puppeteer - index.d.ts 發現寫了一個ExpectPuppeteer接口,這裏裏面就有咱們全部的api,咱們順便點看任意一個js文件看看,目錄(expect-puppeteer - lib - options.js),咱們就看看這個文件,看不懂不要緊,可是大概咱們能猜出,就是控制運行時間的嘛,實際工做中一個case可能會寫大量的waitfor()是否是很麻煩,因此,我建議,吧options.js的timeout設置長一點,這樣方便更準確的尋找頁面元素
  4. api很少作介紹,照着api文檔就會了,比較簡單
options.js -> 修改timeout爲2s
let defaultOptionsValue = {
  timeout: 2000
};

3、PO 模式

問:什麼是PO模式?api

答:概念自行百度,我就不粘貼了,我想稍微寫過一點UI自動化的,應該都會多多少少了解一點,通俗的說,咱們把元素,方法,測試case 分開寫,這樣方便咱們去管理,邏輯也不叫清晰,具體下面拿實例來講明dom

4、實例 (以同程網站爲實例)

今天咱們來寫,從首頁進入ly.com,點擊機票 - 國內機票 - 驗證機票默認彈框async

(一)個人腳本目錄

-----__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);
})

結果
工具

相關文章
相關標籤/搜索