定制流水线式执行web自动化测试的工具。
不同插件的默认值是按照如下目录结构来设置默认值的,也是我们推荐的目录结构。
- project_path
- DevOps
- devops-app (用于DevOps执行自动化测试和输出测试产物)
- matman-app (端对端测试)
- mockstar-app (数据打桩)
- README.md
- src (项目源码)
- test (测试用例)
- package.json
- DevOps
启动自动化测试。
dwtPath
,String
,DWT
(DevOps for Web Test) 目录,流水线式执行web自动化测试和输出测试产物的路径,如果插件传入了相对路径,则是相对于该路径而言config
,Object
,配置参数config.workspacePath
,String
,工作区间的路径,即项目的根目录,如果是 git 项目,则是 git 仓库的根目录config.outputPath
,String
,测试产物输出目录,默认为path.join(dwtPath, 'output')
config.plugins
,Array
,插件列表,启动测试之后,会依次执行插件的生命周期事件(init
- >beforeRun
- >run
- >afterRun
)
插件的基础类,所有的插件需要继承它。启动测试之后,会依次执行插件的生命周期事件(init
- > beforeRun
- > run
- > afterRun
)。
name
,String
,插件的名字opts
,Object
,插件的配置,不同插件可能有不同的区别opts.shouldSkip
,Boolean|Function
,是否应该跳过执行,当其为函数时,会传入参数testRecorder
初始化插件
运行自动化测试之前执行
运行自动化测试
运行自动化测试之后执行
判断是否应该运行该插件
工程项目插件,即我们原始的工程,自动化测试时,我们可能需要安装依赖和构建等操作。
name
,String
,插件的名字,默认值为pluginProject
opts
,Object
,插件的配置,不同插件可能有不同的区别opts.shouldSkip
,Boolean|Function
,是否应该跳过执行,当为函数时,接受testRecord
参数opts.rootPath
,String
,项目根路径,默认值:由于我们推荐DWT
路径为DevOps/devops-app
,因此默认值为path.join(dwtPath, '../../')
opts.usePort
,Boolean
,是否需要端口,有些开发场景时,启动项目会使用一个端口,例如 webpack 构建项目时使用热更新模式下,就需要一个端口,默认值:false
opts.port
,Number
,端口号,如果设置了opts.usePort
为true
,若没有传入,则会自动找到未被占用的端口opts.installCmd
,String|Function
,安装依赖时执行的命令,当其为函数时,会传入参数testRecorder
,默认值为function (testRecord) { return 'npm install'; }
opts.buildCmd
,String|Function
,构建项目时执行的命令,当其为函数时,会传入参数testRecorder
和port
,默认值为function (testRecord, port) { return 'npm start'; }
opts.buildCompleteCheck
,Function
,检查构建是否完成,会传入参数data
,代表的时控制台输出,在某些场景下,可以通过判断某些输出,来判断构建已经结束,如果返回true
,则将强制结束构建,默认值为function (data) { return false; }
初始化插件,需要处理的事情包括:
- 将
rootPath
修改为绝对路径
运行自动化测试之前执行,需要处理的事情包括:
await this.clean(testRecord);
运行自动化测试,需要处理的事情包括:
// 进入项目中安装依赖
await this.install(testRecord);
// 获取 project 的端口号
await this.findPort(testRecord);
// 构建项目
await this.build(testRecord);
运行自动化测试之后执行,需要处理的事情包括:
await this.clean(testRecord);
安装依赖,执行 this.installCmd
命令
构建项目,执行 this.buildCmd
命令
清理,需要处理的事情包括:
- 清理端口号
port
,避免该端口号被占用
获得可用的端口号,并存储在 this.port
中
单元测试项目插件。
name
,String
,插件的名字,默认值为pluginUnitTest
opts
,Object
,插件的配置,不同插件可能有不同的区别opts.shouldSkip
,Boolean|Function
,是否应该跳过执行,当为函数时,接受testRecord
参数opts.runTestPath
,String
,执行单元测试的根路径,默认值:由于我们推荐DWT
路径为DevOps/devops-app
,因此默认值为path.join(dwtPath, '../../')
opts.outputPath
,String
,单元测试结果输出的路径,默认值:path.join(testRecord.outputPath, 'unit_test_report')
opts.coverageOutputPath
,String
,单元测试的覆盖率输出的路径,推荐放在单元测试结果输出文件夹内,默认值:path.join(this.outputPath, 'coverage')
opts.testCmd
,String|Function
,执行测试的命令,当其为函数时,会传入参数testRecorder
,默认值为function (testRecord) { return 'npm test'; }
opts.coverageCmd
,String|Function
,执行测试的命令,当其为函数时,会传入参数testRecorder
和testCmdToExecute
(实际执行的测试的命令),默认值为function (testRecord, testCmdToExecute) { return 'npm run coverage'; }
opts.onBeforeTest
,Function
,在运行测试之前执行的钩子函数,会传入参数testRecorder
和util
opts.testCompleteCheck
,Function
,检查测试过程是否完成,会传入参数data
,代表的时控制台输出,在某些场景下,可以通过判断某些输出,来判断构建已经结束,如果返回true
,则将强制结束构建,默认值为function (data) { return false; }
opts.coverageCompleteCheck
,Function
,检查覆盖率是否完成,会传入参数testRecorder
,由于生成覆盖率文件是异步的,某些时候需要实际去检查所需要的覆盖率文件是否实际已经完成,此时可以用该方法
初始化插件,需要处理的事情包括:
- 将
runTestPath
修改为绝对路径 - 将
outputPath
修改为绝对路径 - 将
coverageOutputPath
修改为绝对路径
运行自动化测试之前执行,暂无。
运行自动化测试,需要处理的事情包括:
// 在运行测试之前执行的钩子函数
if (typeof this.onBeforeTest === 'function') {
await Promise.resolve(this.onBeforeTest.call(this, testRecord, util));
}
// 启动测试
await this.runTest(testRecord);
// 获取单元测试覆盖率
await this.runCoverage(testRecord);
运行自动化测试之后执行,暂无。
启动测试,执行 this.testCmd
命令
构建项目,执行 this.coverageCmd
命令
mockstar 项目插件。
name
,String
,插件的名字,默认值为pluginMockstar
opts
,Object
,插件的配置,不同插件可能有不同的区别opts.shouldSkip
,Boolean|Function
,是否应该跳过执行,当为函数时,接受testRecord
参数opts.rootPath
,String
,项目根路径,默认值:由于我们推荐DWT
路径为DevOps/devops-app
,因此默认值为path.join(dwtPath, '../mockstar-app')
opts.port
,Number
, mockstar 启动端口opts.installCmd
,String|Function
,安装依赖时执行的命令,当其为函数时,会传入参数testRecorder
,默认值为function (testRecord) { return 'npm install'; }
opts.startCmd
,String|Function
,安装依赖时执行的命令,当其为函数时,会传入参数testRecorder
和port
,默认值为function (testRecord, port) { return 'npm install'; }
初始化插件,需要处理的事情包括:
- 将
rootPath
修改为绝对路径 - 自动生成唯一标识
this._processKey
运行自动化测试之前执行:
await this.clean(testRecord);
运行自动化测试,需要处理的事情包括:
// 进入项目中安装依赖
await this.install(testRecord);
// 获取 mockstar 的端口号
await this.findPort(testRecord);
// 启动 mockstar
await this.start(testRecord);
运行自动化测试之后执行:
await this.clean(testRecord);
清理,需要处理的事情包括:
- 清理端口号
port
,避免该端口号被占用
获得可用的端口号,并存储在 this.port
中
安装依赖,执行 this.installCmd
命令
构建项目,执行 this.startCmd
命令
mockstar 项目插件。
name
,String
,插件的名字,默认值为pluginWhistle
opts
,Object
,插件的配置,不同插件可能有不同的区别opts.shouldSkip
,Boolean|Function
,是否应该跳过执行,当为函数时,接受testRecord
参数opts.port
,Number
, whistle 启动端口opts.getWhistleRules
,Function
,获得 whistle 规则,需要返回格式为{name: String, rules: String}
opts.configFileName
,String
,whistle 规则配置文件名,文件中包含了规则信息等,默认值为test.whistle.js
opts.configFile
,String
,whistle 规则配置文件路径,默认值为path.join(testRecord.outputPath, this.configFileName)
初始化插件,需要处理的事情包括:
- 将
configFile
修改为绝对路径 - 自动生成唯一标识
this._processKey
运行自动化测试之前执行:
await this.clean(testRecord);
运行自动化测试,需要处理的事情包括:
// 获取 whistle 的端口号
await this.findPort(testRecord);
// 生成 .whistle.js 配置文件
await this.generateConfigFile(testRecord);
// 启动 whislte
await this.start(testRecord);
// 设置并强制使用指定 whistle 配置规则
await this.use(testRecord);
运行自动化测试之后执行:
await this.clean(testRecord);
根据 this.getWhistleRules
获得的代理规则,生成一个本地的 whistle 配置文件。
清理,需要处理的事情包括:
- 清理端口号
port
,避免该端口号被占用
获得可用的端口号,并存储在 this.port
中
启动 whistle,启动命令格式为 w2 start -S ${this._processKey} -p ${this.port}
。
使用指定的 whistle 规则配置文件,启动命令格式为 w2 use ${this.configFile} -S ${this._processKey} --force
。
端对端测试项目插件。
name
,String
,插件的名字,默认值为pluginE2ETest
opts
,Object
,插件的配置,不同插件可能有不同的区别opts.shouldSkip
,Boolean|Function
,是否应该跳过执行,当为函数时,接受testRecord
参数opts.runTestPath
,String
,执行端对端测试的根路径,默认值:由于我们推荐DWT
路径为DevOps/devops-app
,因此默认值为path.join(dwtPath, '../../')
opts.outputPath
,String
,单元测试结果输出的路径,默认值:path.join(testRecord.outputPath, 'e2e_test_report')
opts.coverageOutputPath
,String
,单元测试的覆盖率输出的路径,推荐放在单元测试结果输出文件夹内,默认值:path.join(this.outputPath, 'coverage')
opts.testCmd
,String|Function
,执行测试的命令,当其为函数时,会传入参数testRecorder
和whistlePort
,默认值为function (testRecord, whistlePort) { return 'npm test'; }
opts.onBeforeTest
,Function
,在运行测试之前执行的钩子函数,会传入参数testRecorder
和util
opts.testCompleteCheck
,Function
,检查测试过程是否完成,会传入参数data
,代表的时控制台输出,在某些场景下,可以通过判断某些输出,来判断构建已经结束,如果返回true
,则将强制结束构建,默认值为function (data) { return false; }
opts.getWhistlePort
,Function
,获得whistle
的端口号,默认值为function (testRecord) { return 0; }
opts.matmanAppPath
,String
,matman 应用的根路径,默认值:由于我们推荐DWT
路径为DevOps/devops-app
,因此默认值为path.join(dwtPath, '../matman')
opts.matmanAppInstallCmd
,String|Function
,matman 应用安装依赖时执行的命令,当其为函数时,会传入参数testRecorder
,默认值为function (testRecord) { return 'npm install'; }
opts.matmanAppBuildCmd
,String|Function
,matman 应用构建项目时执行的命令,当其为函数时,会传入参数testRecorder
,默认值为function (testRecord,) { return 'npm run build'; }
初始化插件,需要处理的事情包括:
- 将
runTestPath
修改为绝对路径 - 将
matmanAppPath
修改为绝对路径 - 将
outputPath
修改为绝对路径 - 将
coverageOutputPath
修改为绝对路径
运行自动化测试之前执行,暂无。
运行自动化测试,需要处理的事情包括:
// matman-app 安装依赖
await this.matmanAppInstall(testRecord);
// 测试之前需要 matman-app 构建
await this.matmanAppBuild(testRecord);
// 启用 xvfb
await this.startXvfb(testRecord);
// 在运行测试之前执行的钩子函数
if (typeof this.onBeforeTest === 'function') {
await Promise.resolve(this.onBeforeTest.call(this, testRecord, runCmd));
}
// 启动测试
await this.runTest(testRecord);
// 停止 xvfb
await this.stopXvfb(testRecord);
// 处理测试覆盖率
await this.createE2ECoverage(testRecord);
// copy build to output
await this.copyBuildOutputToArchive(testRecord);
运行自动化测试之后执行,暂无。
启动 xvfb,注意只有在 process.env.USE_XVFB
存在时才会处理
关闭 xvfb,注意只有在 process.env.USE_XVFB
存在时才会处理
启动 matman-app 的安装依赖,执行 this.matmanAppInstallCmd
命令
启动 matman-app 的构建,执行 this.matmanAppBuildCmd
命令
启动测试,执行 this.testCmd
命令
分析并生成测试覆盖率数据
将端对端测试运行结果拷贝到归档目录中
归档项目插件。
name
,String
,插件的名字,默认值为pluginArchive
opts
,Object
,插件的配置,不同插件可能有不同的区别opts.shouldSkip
,Boolean|Function
,是否应该跳过执行,当为函数时,接受testRecord
参数opts.getPlugins
,Function
,获取插件,接受testRecord
参数, 返回{ pluginE2ETest: PluginE2ETest, pluginUnitTest: PluginUnitTest, pluginWhistle: PluginWhistle}
其他的属性:
this.rootPath
,String
,归档文件夹路径,值为testRecord.outputPath
this.outputZipPath
,String
,归档文件夹路径,值为path.join(this.rootPath, 'output.zip')
this.indexHtmlPath
,String
,归档文件夹路径,值为path.join(this.rootPath, 'index.html')
this.indexHtmlDataPath
,String
,归档文件夹路径,值为path.join(this.rootPath, 'index-html.json')
this.testRecordPath
,String
,归档文件夹路径,值为path.join(this.rootPath, 'test-record.json')
初始化插件,需要处理的事情包括:
- 将
rootPath
修改为绝对路径 - 将
outputZipPath
修改为绝对路径 - 将
indexHtmlPath
修改为绝对路径 - 将
indexHtmlDataPath
修改为绝对路径 - 将
testRecordPath
修改为绝对路径
运行自动化测试之前执行:
await this.clean(testRecord);
运行自动化测试,需要处理的事情包括:
// 保存自定义报告入口文件
this.saveOutputIndexHtml(testRecord, pluginMap);
// 保存 testRecord 内容
this.saveTestRecordContent(testRecord);
// 压缩下 output 目录
await this.compressDir(testRecord);
运行自动化测试之后执行:
await this.clean(testRecord);
清理,需要处理的事情包括:
- 删除上次缓存的文件内容,即清空
this.rootPath
目录
获得端对端测试报告
获得单元测试报告
保存自定义报告入口文件
保存 testRecord 内容
压缩保存测试输出文件
自定义插件。
name
,String
,插件的名字,默认值为pluginCustom
opts
,Object
,插件的配置,不同插件可能有不同的区别opts.shouldSkip
,Boolean|Function
,是否应该跳过执行,当为函数时,接受testRecord
参数opts.onInit
,Function
,init
方法中的钩子函数,接受testRecord
参数opts.onBeforeRun
,Function
,beforeRun
方法中的钩子函数,接受testRecord
参数opts.onRun
,Function
,run
方法中的钩子函数,接受testRecord
参数opts.onAfterRun
,Function
,afterRun
方法中的钩子函数,接受testRecord
参数
初始化插件,调用 this.onInit
运行自动化测试之前执行,调用 this.onBeforeRun
运行自动化测试,调用 this.onRun
运行自动化测试之后执行,调用 this.onAfterRun