用 jest + gitlab-ci 搭建前端UI自动化测试环境

用 jest + gitlab-ci 搭建前端UI自动化测试环境

前言

下面以 github创建仓库为例 测试领域中比较麻烦的就是前端UI测试
gitlab地址: https://gitlab.com/yunqiangwu/devops-test.git

前置要求

  • 拥有gitlab账号(公司内部的也可以)
  • gitlab 已经配置了 CI Runner(具体方法百度)

功能实现效果

  • 修改过测试用例后push触发自动测试
  • 测试完成后生成测试报告
  • 测试报告部署到 git pages 上
  • 发送邮件通知测试结果,以及报告查看链接

先看下我在实际项目中的效果,下面是我收到的自动化测试完成后的邮件:

预览地址
自动化测试报告
测试覆盖率报告

创建项目

1
2
3
4
5
6
7
8
9
mkdir devops-test
cd devops-test
npm init # 初始化 `node` 项目
# 这里会要你输入信息,节约时间,一路回车
# 下面的操作我用的是cnpm
cnpm i jest jest-report nightmare --save
cnpm i babel-jest babel-plugin-import babel-plugin-transform-class-properties babel-plugin-transform-decorators-legacy babel-plugin-transform-runtime
babel-preset-env --save-dev
#

重点依赖库介绍:

  • jest 测试框架
  • nightmare 浏览器运行库
  • jest-report 测试报告生产库

配置jest环境

  1. package.jsonscripts 中叫如 test 命令
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "devops-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest e2e.js"
},
"author": "",

// ...

}
  1. 创建jasmine配置文件 项目根目录/tests/jasmine.js 配置测试超时时间
1
jasmine.DEFAULT_TIMEOUT_INTERVAL = 12000; // 只有这一行内容
  1. package.json 中添加两个字段 babeljest ,这样就能跑 es6 的代码
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
{
"name": "devops-test",
"version": "1.0.0",

// ...

"babel": {
"presets": [
"env"
],
"plugins": [
"transform-decorators-legacy",
"transform-class-properties",
"transform-es2015-destructuring"
]
},
"jest": {
"testResultsProcessor": "jest-report",
"setupTestFrameworkScriptFile": "<rootDir>/tests/jasmine.js",
"testMatch": [
"**/?(*.)(spec|test|e2e).js?(x)"
]
},

// ...

}

编写测试用例

测试github登录功能<rootDir>/src/e2e/test-github-login.e2e.js

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
import Nightmare from 'nightmare';
import { helperBuilder } from 'jest-report';

describe('Login', () => {
let page;
beforeEach(() => {
page = Nightmare({ show: true }).viewport(1024, 768);
page.goto('https://github.com/login');
});

// afterEach(() => {
// if(page){
// page.halt();
// page = null;
// }
// });

it('should login with failure', async() => {
const reportHelper = helperBuilder('Login', 'should login with failure');

reportHelper.monitorPage(page);
await page
.type('#login_field', 'mockuser')
.type('#password', 'wrong_password')
.click('input[type="submit"]')
.wait('#js-flash-container > div > div'); // should display error
await page.screenshot(reportHelper.genPicturePath());
const text = await page.wait('#js-flash-container > div > div')
.evaluate(() => document.body.innerText)
.end();
await page.end();
expect(text).toContain('Incorrect username or password');
});

it('should login successfully', async() => {
const reportHelper = helperBuilder('Login', 'should login successfully');

reportHelper.monitorPage(page);
await page
.type('#login_field', '正确用户名')
.type('#password', '正确密码')
.click('input[type="submit"]')
.wait('#your_repos > div > div.boxed-group-action > a'); // should display error
await page.screenshot(reportHelper.genPicturePath());
const title = await page
.evaluate(() => document.title)
.end();
await page.end();

expect(title).toBe('GitHub');
});
});

运行测试生成测试报告

1. 执行命令npm test,运行测试脚步

2. 生成测试报告

生成的文件路径默认是 `<rootDir>/dist/test-report`
 

3. 测试报告效果

添加项目到gitlab

1
2
3
4
5
6
git init
git remote add origin https://gitlab.com/yunqiangwu/devops-test.git
git add .
git commit -m "Initial commit"
git push -u origin master

添加到持续集成

自动部署测试报告 到gitlab pages 网页

1
cnpm i --save-dev gh-pages

package.json 添加 scripts

1
2
3
4
"scripts": {
"test": "jest e2e.js",
"site": "gh-pages -d dist"
},

配置邮件通知工具
<rootDir>/tool/emailnotice.sh

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
#!/bin/bash
MAIL_FROM='supportman@yeah.net'
# [[ $MAIL_TO == "" ]] && export MAIL_TO=$1
MAIL_TO_ARR_ARG=$1
MAIL_SUBJECT=$2
shift 2
MAIL_CONTENT=$*
MAIL_CONTENT_FILE="/tmp/`/bin/date +%s`.txt"
MAIL_SMTP='smtp://smtp.yeah.net'
MAIL_USER='supportman@yeah.net'
MAIL_PASSWORD='wu950429'

OLD_IFS="$IFS"
#设置分隔符
IFS=","
MAIL_TO_ARR=($MAIL_TO_ARR_ARG)
IFS="$OLD_IFS"
split_1()
{

MAIL_TO=$1
echo "发送邮件到:"$1
# return 0;
# create mail content file
echo "From:${MAIL_FROM}
To:$MAIL_TO
Subject: $MAIL_SUBJECT

$MAIL_CONTENT
"> ${MAIL_CONTENT_FILE}

# send mail
curl -s --url "${MAIL_SMTP}" --mail-from "${MAIL_FROM}" --mail-rcpt ${MAIL_TO} --upload-file ${MAIL_CONTENT_FILE} --user "${MAIL_USER}:${MAIL_PASSWORD}"

}

for s in ${MAIL_TO_ARR[@]}
do
split_1 "$s"
done

rm -rf ${MAIL_CONTENT_FILE}

gitlab-ci.yml 配置文件 .gitlab-ci.yml

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

image: jonneywu/node-xvfb-cnpm

cache:
key: "$CI_REPOSITORY_URL"
paths:
- node_modules
- dist

stages:
- test
- email


test:
script:
- if [ ! -d node_modules ]; then cnpm i; fi
- if [ ! -d node_modules/nightmare ]; then cnpm i nightmare; fi
- export DISPLAY=':99.0'
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
- npm run test && echo test FAIL
- kill -9 `ps -ef | grep Xvfb| grep -v grep|awk '{print $2}'`
- npm run site
stage: test
only:
- master
artifacts:
paths:
- dist

email:
script:
- echo email
- if [[ ! -n $MAIL_TO ]]; then export MAIL_TO=842269153@qq.com,yunqiang.wu@hand-china.com ; fi
- export root_url=http://`cat CNAME`
- export test_passed_rate=$(printf "%.2f" `cat dist/test-report/testResultData.json | jq '100*.numPassedTests/.numTotalTests'`)%
- bash ./tool/emailnotice.sh $MAIL_TO '前端自动化测试' `echo -e " 测试分支: ${CI_COMMIT_REF_NAME} \r\n 提交人:${GITLAB_USER_NAME} \r\n 测试通过率:${test_passed_rate} \r\n 触发来源:${CI_PIPELINE_SOURCE} \r\n 测试环境:mockApi环境 \r\n\r\n预览地址: ${root_url}/index.html \r\n 自动化测试报告:${root_url}/test-report/reporter.html \r\n 测试覆盖率报告: ${root_url}/coverage/lcov-report/index.html \r\n"`
stage: email
only:
- master
artifacts:
paths:
- dist

注意事项

  • react 开发是如果用了css modules的技术开发时,会把 class 名换掉 ,测试框架就无法通过 css selector 找到并操作 Dom元素,在开发时,可以为主要控制的dom节点给iddata-custom 自定义属性,比如用户名输入框、登录按钮,
  • 我这里的介绍的自动化测试,只是测试系统的功能交互是否能正常,如果需要靠自动化测试来判断UI界面的样式对比是符合要求,可能还是要靠人力测试,不过测试的目的保证系统功能成正常使用不出BUG。

参考