Day27 - 铁人付外挂测试验收(三)- 端对端测试

曾经做过一个专案,顾客把商品加入购物车後,可以同时选择要加入几笔商品,然後在结帐页的时候需要根据商品数量来增加报名资料的栏位,也就是说如果购物车里面有三笔资料,一样的报名表单就需要填写三份不同的客户资料,有点类似旅行社在报名前需要填写所有成员的资料一样。

然後某天接到客户反应说当只有一位报名时功能正常,但只要是多位报名会有问题,我就依照惯例开始手动测试,每一个报名要填的栏位有 8 个,所以每测试一次我至少要填 8 x 2 = 16 个栏位,用浏览器记忆的功能输入或许不会花太多时间,但万一客户反应同时报名五个人以上会有问题,我就要填 40 个以上的栏位了,在找寻有没有更快的测试方法时,才知道有端对端测试工具这种好物。

所谓的端对端测试就是透过 JS 来自动操纵浏览器来依序执行页面跳转、按钮点击、填写表单等真实使用者的浏览行为,它可以带来以下好处:

  • 确保我们写好的程序码是可以被使用者正常操作的
  • 测试程序码写好一次後可以重复利用
  • 减少手动测试的人为错误
  • 减少手动测试的时间
  • 安排固定时间自动执行测试
  • 提升程序码品质

现在市面上有超多厂商在提供这样端对端测试的 SaaS 服务,像是 Ghost Inspector、Lambadtest、Usertrace 等等,只要搜寻 auto test tool 就可以找到一大堆,而本文要介绍的是 WooCommerce 官方提供的 wc-e2e-page-object。

它是一个 JS 的套件,整合断言库 Chai.js 以及 WooCommerce 常见的操作行为,像是前台加入购物车、点击结帐按钮,或是後台的新增商品、编辑订单资料等等,都可以透过这个套件来进行操作。

安装套件

首先为了跟 PHPUnit 的资料夹区分,我们新建一个 e2e 资料夹,里面会放 JS 的测试place-order.js,接着新增 .babelrc 贴入并以下内容:

{
    "presets": [
        "es2015",
        "stage-2"
    ],
    "plugins": [
        "add-module-exports"
    ]
}

wc-e2e 套件需经由 NPM 安装,须确保电脑可执行 Node.js,切换到外挂根目录下,新增 package.json 档,并贴入以下内容:

{
  "name": "iron-pay",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "test": "cross-env NODE_CONFIG_DIR='./config' BABEL_ENV=commonjs mocha e2e/place-order.js --compilers js:babel-register --recursive"
  },
  "dependencies": {},
  "devDependencies": {
    "babel": "^6.5.2",
    "babel-cli": "^6.14.0",
    "babel-eslint": "^7.0.0",
    "babel-plugin-add-module-exports": "^0.2.1",
    "babel-preset-es2015": "^6.14.0",
    "babel-preset-stage-2": "^6.13.0",
    "chai": "^3.5.0",
    "chai-as-promised": "^6.0.0",
    "config": "^1.24.0",
    "cross-env": "^3.0.0",
    "istanbul": "^1.0.0-alpha",
    "mocha": "^3.0.2",
    "wc-e2e-page-objects": "0.2.2",
    "chromedriver": "^93.0.1"
  }
}

需要注意的地方是 scripts 的地方,要把 test 路径设定为我们的 e2e 资料夹里面的测试程序码 place-order.js

"test": "cross-env NODE_CONFIG_DIR='./config' BABEL_ENV=commonjs mocha e2e/place-order.js --compilers js:babel-register --recursive"

最後是新增一个 config 资料夹里面放跑端对端测试时的设定档 default.json

{
    "url": "https://woocommerce.test",
    "users": {
        "admin": {
            "username": "woocommerce",
            "password": "password"
        },
        "customer": {
            "username": "",
            "password": ""
        }
    },
    "startBrowserTimeoutMs": 30000,
    "mochaTimeoutMs": 120000
}

url 是本机 WordPress 的路径,并提供管理者的登入帐密,完成以上资料准备後,就能进行套件的安装:

iron-pay$ npm install

安装完成後外挂资料夹结构如下:

iron-pay
├── composer.json
├── composer.lock
├── config
│   └── default.json
├── e2e
│   └── place-order.js
├── iron-pay.php
├── node_modules
├── package-lock.json
├── package.json
├── phpunit.xml
├── src
│   ├── Gateways
│   ├── Options.php
│   ├── Posts
│   └── Utility.php
├── tests
│   ├── Gateways
│   ├── OptionsTest.php
│   ├── Posts
│   └── UtilityTest.php
└── vendor

撰写测试程序

接着我们要撰写测试程序码,我们的测试流程为将商品加入购物车 -> 确保购物车内有该商品 -> 进入结帐页 -> 选择铁人付信用卡金流 -> 按下结帐按钮成立订单

开启 place-order.js,贴入以下测试的架构:

import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';

// Helper objects for performing actions.
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';

// We're going to use the ShopPage and CartPage objects for this tutorial.
import { ShopPage, CartPage, CheckoutPage } from 'wc-e2e-page-objects';

chai.use( chaiAsPromised );
const assert = chai.assert;

let manager;
let driver;

test.describe( '铁人付信用卡测试', function() {

    // Set up the driver and manager before testing starts.
    test.before( 'open browser', function() {
        this.timeout( config.get( 'startBrowserTimeoutMs' ) );

        manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
        driver = manager.getDriver();

        helper.clearCookiesAndDeleteLocalStorage( driver );
    } );

    this.timeout( config.get( 'mochaTimeoutMs' ) );

    // 测试

    test.after( 'quit browser', () => {
        manager.quitBrowser();
    } );

} );

基本架构为先引入测试要用的套件,分别汇入 ShopPage、CartPage、CheckoutPage 三个由 wc-e2e-page-objects 提供的模组,这些模组都有各自的方法来进行介面的互动。

测试的任务从 test.describe() 开始,先定义这个任务的名称,然後开启浏览器、检查是否逾时,到最後关闭浏览器,介於 test.before()test.after() 中间就是我们要执行的动作。

首先是将商品加入购物车:

// 略
test.describe( '铁人付信用卡测试', function() {

    // Set up the driver and manager before testing starts.
    test.before( 'open browser', function() {
      // 略
    } );

    this.timeout( config.get( 'mochaTimeoutMs' ) );

    // 测试加入购物车
	test.it( '将商品加入购物车', () => {
        const shopPage = new ShopPage( driver, { url: manager.getPageUrl( '/shop' ) } );
        const added_product = shopPage.addProductToCart( 'Flying Ninja' );
        assert.eventually.ok( added_product );
    } );

    test.after( 'quit browser', () => {
      // 略
    } );

} );

宣告 ShopPage 的实例,并且将本机页面跳转至 shop 页面,接下来使用 ShopPage 物件的 addProductToCart() 方法,将商品 Flying Ninja 加入购物车,该方法会回传布林值,所以用断言 assert.eventually.ok( added_product ) 检查是否回传为 true,完成後执行 npm run test,就会看到 Chrome 浏览器被启用,并且自动执行加入购物车的动作,并在命令列里面看到执行成功的讯息:

iron-pay$ npm run test

> [email protected] test /Users/oberonlai/Sites/woocommerce/wp-content/plugins/iron-pay
> cross-env NODE_CONFIG_DIR='./config' BABEL_ENV=commonjs mocha e2e/place-order.js --compilers js:babel-register --recursive



  铁人付信用卡测试
    ✓ 将商品加入购物车 (1936ms)


  1 passing (4s)

接下来加入第二个任务,让页面自动跳转到购物车并且确认里面有刚刚加入的商品名称:

// 略
test.describe( '铁人付信用卡测试', function() {

    // Set up the driver and manager before testing starts.
    test.before( 'open browser', function() {
      // 略
    } );

    this.timeout( config.get( 'mochaTimeoutMs' ) );

    // 测试加入购物车
	test.it( '将商品加入购物车', () => {
      // 略
    } );
	
	// 测试购物车有正确加入商品
	test.it( '前往购物车', () => {
        const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
        const has_product = cartPage.hasItem( 'Flying Ninja' );
        assert.eventually.ok( has_product );
    } );

    test.after( 'quit browser', () => {
      // 略
    } );

} );

这边我们使用的是 CartPage 物件,并且用它的 hasItem() 方法来检查购物车里面是否有 Flying Ninja 这个商品,如果有的话就能通过断言:

iron-pay$ npm run test

[email protected] test /Users/oberonlai/Sites/woocommerce/wp-content/plugins/iron-pay
> cross-env NODE_CONFIG_DIR='./config' BABEL_ENV=commonjs mocha e2e/place-order.js --compilers js:babel-register --recursive



  铁人付信用卡测试
    ✓ 将商品加入购物车 (2302ms)
    ✓ 前往购物车 (831ms)


  2 passing (5s)

最後是测试铁人付信用卡结帐,测试流程是选择信用卡并点击前往付款按钮:

// 略
test.describe( '铁人付信用卡测试', function() {

    // Set up the driver and manager before testing starts.
    test.before( 'open browser', function() {
      // 略
    } );

    this.timeout( config.get( 'mochaTimeoutMs' ) );

    // 测试加入购物车
	test.it( '将商品加入购物车', () => {
      // 略
    } );
	
	// 测试购物车有正确加入商品
	test.it( '前往购物车', () => {
       // 略
    } )
	
	// 测试能够使用铁人付前往结帐
	test.it( '前往结帐页并下订单', () => {
        const checkoutPage = new CheckoutPage( driver, { url: manager.getPageUrl( '/checkout' ) } );
        const select_payment = checkoutPage.selectPaymentMethod( '信用卡' );
        const place_order = checkoutPage.placeOrder();
        
        assert.eventually.ok( select_payment );
        assert.eventually.ok( place_order );
    } );

    test.after( 'quit browser', () => {
      // 略
    } );

} );

这边使用 CheckoutPage 物件,并利用 selectPaymentMethod()placeOrder() 这两个方法来执行选择付款方式与结帐的动作,要注意的地方是不管是哪种方法,要传送的参数都是页面上看得到的名称,而非栏位的 id 或 value 值,画面上看到什麽就传什麽,执行结果如下:

iron-pay$ npm run test

> [email protected] test /Users/oberonlai/Sites/woocommerce/wp-content/plugins/iron-pay
> cross-env NODE_CONFIG_DIR='./config' BABEL_ENV=commonjs mocha e2e/place-order.js --compilers js:babel-register --recursive



  铁人付信用卡测试
    ✓ 将商品加入购物车 (1972ms)
    ✓ 前往购物车 (487ms)
    ✓ 前往结帐页并下订单 (786ms)


  3 passing (5s)

这样就能确保使用铁人付信用卡是可以正常建立订单的。

当我们写了测试来检查自己的程序码时,目的不是消除所有错误,也并非是写了测试功能就完全不会有臭虫,而是让我们在修改程序的当下,万一引发某些错误时可以立即透过测试来得知。

像上面的这个测试我们就无法测到金流商 API 发生错误时的情境,但因为有了这些测试,我们可以快速定位到是因为回传资料有误进而追查,而非又要翻原始码去逐一定位问题所在,有了测试我们可以把除错范围缩小,减少除错的时间。

当我们在本机测试完成後,接下来就要进入测试机的阶段,下一篇会介绍外挂的部署流程,以及如何使用 VPS 建置测试站。

本文同步发表於:https://oberonlai.blog/tw/woocommerce-payment-e2e/


<<:  Day27:Backtracking -回溯法

>>:  Day 12 - UML — 系统设计不可不知的 UML

[Android Studio 30天自我挑战] ImageView元件介绍

现在在手机或是平板上都会许多图片的应用,这次介绍ImageView与ImageButton这两个元件...

我们的基因体时代-AI, Data和生物资讯 Day23- 基因注释资料在Bioconductor中视觉化之呈现:Gviz

上一篇我们的基因体时代-AI, Data和生物资讯 Day22- 基因注释资料在Bioconduct...

DAY28 CNN(卷积神经网路 续二)

昨天介绍完CNN卷积神经网路正向传播程序,今天要来研究CNN卷积神经网路反向传播程序: 首先在上次全...

Proof of Work 工作量证明

前言 在区块链中每条链或多或少都各自发展出自己不同的共识机制,例如 Proof of Work、Pr...

如何将 OLM 导入 Excel 并附上附件?

摘要:要不要将OLM 导入到带有附件的Excel 中?如果是这种情况,您的搜索现已完成。这篇文章将向...