1. newman?report?
1.1 newman是什么
newman时一款基于Node.js开发的可以运行postman的工具,使用Newman可以直接从命令行运行postman集合。更多介绍可以参看这里。
1.2 reporter是什么
reporter决定了newman输出的形式,不同的repoter以不同的形式输出newman的执行结果。如果自带的reporter和第三方reporter都不能满足自己需要,就要DIY自己的reporter了。
2. 如何编写自己的repoter
对于如何编写自己的report, 文档上写的非常简略。
https://www.npmjs.com/package/newman#creating-your-own-reporter
只是说了repoter是以node模块的形式存在,模块该以什么形式命名,如何打包。那么具体的模块内部逻辑该如何写呢?
2.1 思路
起先我也不知如何入手,读了两个第三方reporter后,发现思路是这样的:
newman在执行过程中提供了各种事件的回调,比如集合开始执行、test执行前、test执行后、集合执行结束等等。你只需要在适合的事件上注册自己的回调函数,在函中记下自己要的信息。在集合执行结束后输出即可。
2.2 事件
newman提供的事件在文档上列的比较清楚
https://www.npmjs.com/package/newman#newmanrunevents
比较常用的如下:
- start: 整个集合开始运行
- beforeItem:单个条目(item)开始运行前。
一个条目是指prerequest->request->test的整个过程。 - item: 单个条目运行结束
- beforeRequest:发送http请求之前。注意,是任何http请求。我们不但在request中会发http请求。在prerequest, tests中也可能会发送http请求。
- request:请求结束拿到响应后
- beforeTest:测试脚本开始前
- test:测试脚本结束后
-
assertion:每个test结束后。注意是一个test,每个request可以包含多个test。
image
上例中,assertion会被触发3次。
- beforeDone:整个执行(run)完成前
所有的事件回调函数可以接收两个参数:
- err: 出错信息
- info: 执行信息。每个事件的执行信息包括哪些内容是不同的,文档中没有写,只能自己console.log输出看一下了。
比如, assertion的info包括了如下诸多信息
{
cursor: {
position: 0,
... ...
ref: '085d9877-fdb0-4097-a9d4-88092bfd3e21',
httpRequestId: '456d2ac5-4b54-4bee-90cb-21609c70b186',
scriptId: '144be8cb-482b-4c09-be43-5fb93ad58c9e',
execution: 'f58760ef-64f1-47fb-b5fd-1b0e6acc17f7'
},
//当前test的名子
assertion: 'http code is 200',
skipped: false,
//test错误信息, 当test中有断言出错时,会有此项
error: {
name: 'AssertionError',
message: 'expected response to have status code 302 but got 200',
showDiff: true,
actual: 200,
expected: 302,
stack: 'AssertionError: expected response to have status code 302 but got 200\n' +
' at eval (eval at exec (evalmachine.<anonymous>:13180:2548), <anonymous>:3:25)\n' +
' at Postman.n.test (evalmachine.<anonymous>:81:386)\n' +
' at Object.eval (eval at exec (evalmachine.<anonymous>:13180:2548), <anonymous>:2:4)\n' + ... ...
},
item: PostmanItem {
... ...
}
}
3. 一个例子
单看上面的内容可能你还有些迷糊,那么让我们看来一个实际的例子。
3.1 代码注释
这是一个第三方的reporter, 以csv形式输出结果,取自
https://github.com/matt-ball/newman-reporter-csv/blob/master/index.js
我给它加一些注释,希望对理解如何编写reporter有帮助。
let log
const logs = []
//定义自己的记录格式字段(csv的表头),输出结果时过滤用
const columns = [
'iteration',
'collectionName',
'requestName',
'method',
'url',
'status',
'code',
'responseTime',
'responseSize',
'executed',
'failed',
'skipped'
]
const CSV = {
stringify: (str) => {
return `"${str.replace(/"/g, '""')}"`
}
}
/**
* Reporter that outputs basic logs to CSV (default: newman-run-report.csv).
*
* @param {Object} newman - The collection run object, with event hooks for reporting run details.
* @param {Object} options - A set of collection run options.
* @param {String} options.export - The path to which the summary object must be written.
* @returns {*}
*/
module.exports = function newmanCSVReporter (newman, options) {
//每个item执行前清空log
newman.on('beforeItem', (err, e) => {
if (err) return
log = {}
})
//请求之前记录集合名,迭代次,item名,url等信息
newman.on('beforeRequest', (err, e) => {
if (err) return
const { cursor, item, request } = e
Object.assign(log, {
collectionName: newman.summary.collection.name,
iteration: cursor.iteration + 1,
requestName: item.name,
method: request.method,
url: request.url.toString()
})
})
//请求后记录http返回码,请求时间,请求大小等信息
newman.on('request', (err, e) => {
if (err) return
const { status, code, responseTime, responseSize } = e.response
Object.assign(log, { status, code, responseTime, responseSize })
})
//每个test后记录执行情况
newman.on('assertion', (err, e) => {
const { assertion } = e
const key = err ? 'failed' : e.skipped ? 'skipped' : 'executed'
log[key] = log[key] || []
log[key].push(assertion)
})
//每个item执行完,收集整条log
newman.on('item', (err, e) => {
if (err) return
logs.push(log)
})
//run执行完之前
newman.on('beforeDone', (err, e) => {
if (err) return
newman.exports.push({
//repoter名子
name: 'csv-reporter',
//默认文件名形式
default: 'newman-run-report.csv',
//输出路径
path: options.export,
//输出内容
content: getResults()
})
console.log('CSV write complete!')
})
}
//处理logs中的每条log, 将其转为csv格式
function getResults () {
const results = logs.map((log) => {
let row = []
Object.keys(log).forEach((key) => {
const val = log[key]
const index = columns.indexOf(key)
const rowValue = Array.isArray(val)
? val.join(', ')
: String(val)
row[index] = CSV.stringify(rowValue)
})
return row.join(',')
})
results.unshift(columns.join(','))
return results.join('\n')
}
3.2 说明
- 回调中提供的信息(上述代码中的e)所包括的内容,从上面的例子中可以窺其一斑。
- 上面的代码其实是有bug的。我们知道对于一个item来说,其执行顺序为:
pre-request脚本
发送请求, 收到响应
test脚本
所以,如果test脚本中也有http请求时,同样会触发beforeRequest, request两个事件。这样就会覆盖我们希望记录的真实请求的相应内容。改正的思路也比较简单,test部分的事件发生顺序为:
beforeTest
beforeRequest
request
test
所以,我们只需要在beforeTest中设置测试标识,在beforeRequest,request中进行检测,如果是测试过程则跳过信息记录。最后在test中清理测试标识即可。核心思想代码如下:
module.exports = function newmanCSVReporter (newman, options) {
//全局标记
var inTest = false;
newman.on('beforeTest', (err, e)=>{
//测试开始前设置标识
inTest = true;
});
newman.on('test', (err, e)=>{
//测试结后清理标识
inTest = false;
});
newman.on('beforeRequest', (err, e) => {
if (err || inTest) return
//之前逻辑不变
})
newman.on('request', (err, e) => {
if (err || inTest) return
//之前逻辑不变
... ...
})
4. 调试
比如你写了一段3.1中的代码,想要调试,如何进行呢?
- 安装newman
- 安装reporter: newman-reporter-csv
- 找到你的node_modules目录
node_modules/newman-reporter-csv/index.js
上面就是执行使用csv reporter时实际执行的代码。使用下面的方式执行newman, 可以让你对index.js的改动立刻生效。
node_modules/.bin/newman run test.postman_collection.json -r csv
网友评论