挡板实战
上面也算是详细介绍了mountebank、mockjs的用法,那么接下来介绍下我的挡板实现(结合上面的第三张图)
本次讲解的的挡板demo目录为baffle,他的目录结构如下:
路径 | 类型 | 备注 |
---|---|---|
baffle/logs | 目录 | 日志文件目录,存放业务日志和定时任务日志 |
baffle/quartz | 目录 | 存放定时任务,为了实现挡板的回调功能包含log.js(quartz专用)、testquartz.js |
baffle/test | 目录 | 存放挡板服务实现 |
baffle/utils | 目录 | 常用工具类,数据查询、日志输出等,包含db.js、log.js、utils.js |
baffle/imposters.ejs | 文件 | 启动参数 文件 |
baffle/start.sh | 文件 | linux启动脚本,方便服务启动 |
baffle/stop.sh | 文件 | linux停止脚本,方便服务停止 |
baffle/startMac.sh | 文件 | mac启动脚本,里面包含停止脚本 |
挡板回调
利用定时任务实现挡板回调
log.js代码如下:
/**
* 日志打印工具类
*/
var log4js = require('log4js');
log4js.configure({
appenders: {
file: {
type: "dateFile",
filename: './logs/quartz.log', //您要写入日志文件的路径
alwaysIncludePattern: true, //(默认为false) - 将模式包含在当前日志文件的名称以及备份中
//compress: true,//(默认为false) - 在滚动期间压缩备份文件(备份文件将具有.gz扩展名)
pattern: "-yyyy-MM-dd.log", //(可选,默认为.yyyy-MM-dd) - 用于确定何时滚动日志的模式。格式:.yyyy-MM-dd-hh:mm:ss.log
encoding: 'utf-8', //default "utf-8",文件的编码
maxLogSize: 10 //文件最大存储空间,当文件内容超过文件存储空间会自动生成一个文件xxx.log.1的序列自增长的文件
}
},
categories: {
default: {
appenders: ['file'],
level: 'info'
}
}
});
function info(obj) {
logger.info(obj);
}
var logger = log4js.getLogger('log_file');
module.exports = {
info
}
testquartz.js文件如下:
var logger = require('./log');
const cron = require('cron');
var db = require('../utils/db');
var utils = require('../utils/utils');
/**
* 定时任务
*/
// https://www.npmjs.com/package/cron
const CronJob = cron.CronJob;
// CREATE TABLE `prduct` (
// `id` int(255) NOT NULL AUTO_INCREMENT,
// `context` varchar(1000) NOT NULL,
// `flag` smallint(255) NOT NULL DEFAULT '0',
// PRIMARY KEY (`id`)
// ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
//
const prductParam = {
// cronTime: '*/10 * * * * *',
//
cronTime: '0 */5 * * * *',
onTick: function() {
db.query('SELECT * from prduct where flag=0', [], function(ret){
if(ret.length>0){
for(var row in ret){
var formData=ret[row].context;
var parm=[];
parm.push(ret[row].id);
var callRet = utils.call(utils.testUrl.prod.url, formData, function(results) {
logger.info("callRet=" + JSON.stringify(results));
// console.log(results);
db.query("update prduct set flag='1' where flag=0 and id=?", parm, function(err, retu){
logger.info("产品状态变更完成:"+parm);
console.log(err);
});
});
}
}else{
logger.info("没有对应的产品");
console.log("没有对应的产品");
}
});
}
};
// 产品定时推送接口
const prductJob = new CronJob(prductParam);
prductJob.start();
utils工具类
db.js代码如下
var logger = require('./log');
var mysql = require('mysql');
/**
* @param {Object} sql
* @param {Object} arr
* @param {Object} callback
* 执行sql
*/
exports.query = function(sql, arr, callback) {
var connection=getConnection();
//查
connection.query(sql,arr, function(err, result) {
if (err) {
logger.info('[SELECT ERROR] - ', err.message);
return;
}
callback && callback(result);
});
connection.end();
};
/**
* 获取连接
*/
function getConnection(){
var connection = mysql.createConnection({
host: '127.0.0.1',
user: 'root',
password: '123456',
database: 'test_mock'
});
connection.connect();
return connection;
}
log.js代码如下
/**
* 日志打印工具类
*/
var log4js = require('log4js');
log4js.configure({
appenders: {
file: {
type: "dateFile",
filename: './logs/mock.log', //您要写入日志文件的路径
alwaysIncludePattern: true, //(默认为false) - 将模式包含在当前日志文件的名称以及备份中
//compress: true,//(默认为false) - 在滚动期间压缩备份文件(备份文件将具有.gz扩展名)
pattern: "-yyyy-MM-dd.log", //(可选,默认为.yyyy-MM-dd) - 用于确定何时滚动日志的模式。格式:.yyyy-MM-dd-hh:mm:ss.log
encoding: 'utf-8', //default "utf-8",文件的编码
maxLogSize: 10 //文件最大存储空间,当文件内容超过文件存储空间会自动生成一个文件xxx.log.1的序列自增长的文件
}
},
categories: {
default: {
appenders: ['file'],
level: 'info'
}
}
});
function info(obj) {
logger.info(obj);
}
var logger = log4js.getLogger('log_file');
module.exports = {
info
}
utils.js代码如下
var logger = require('./log');
var moment = require('moment');
moment.locale('zh-cn');
function exc(process) {
var request = JSON.parse(process.argv[2]),
response = JSON.parse(process.argv[3]);
var method = request.path.slice(1).replace(new RegExp("/","gm"), "_");
var excutor = method + '(request,response)';
logger.info(excutor);
return excutor;
}
function httpPost(url, formData,callback) {
var superagent = require('superagent');
superagent
.post(url)
.send(formData)
.set('header_key', 'header_value')
.set('Content-Type', 'application/json')
.end(function(err, res) {
if (err) {
logger.info(err);
callback && callback("");
// return "";
} else {
var retData = JSON.parse(res.text)
callback && callback(retData);
// return retData
}
})
}
/**
* 默认的函数
* @param {Object} request
* @param {Object} response
*/
function defaults(request, response) {
console.log(JSON.stringify(response));
var reqData = request.body;
var reqJson = JSON.parse(reqData);
logger.info(reqJson);
}
/**
* 回调地址
*/
var testUrl = {
prod: {
url: '/openapi/prodList',
description: '产品列表'
}
};
function getUuid() {
// 声明变量
var Mock = require('mockjs');
return Mock.mock('@guid');
}
function getDateYYYYMMDD() {
return moment().format('YYYY-MM-DD'); /*现在的时间*/
}
function getDateYYYYMMDDHHMMSS() {
return moment().format('YYYY-MM-DD HH:mm:ss'); /*格式化时间*/
}
function formatDate(val) {
return moment(val).format('YYYY-MM-DD HH:mm:ss'); /*格式化时间*/
}
function subDay(val) {
var _today = moment();
return _today.subtract(val, 'days').format('YYYY-MM-DD'); /*前一天的时间*/
}
function addDay(val) {
var _today = moment();
return _today.add(val, 'days').format('YYYY-MM-DD'); /*明天天的时间*/
}
/**
* @param {Object} url
* @param {Object} formdata
* 调用其他项目的接口
*/
function call(url, formdata, callback) {
logger.info(url);
logger.info(formdata);
var postUrl = "http://localhost:8082" + url;
logger.info("postUrl=" + postUrl);
httpPost(postUrl, formdata, callback);
}
module.exports = {
exc,
httpPost,
defaults,
testUrl,
getUuid,
getDateYYYYMMDD,
getDateYYYYMMDDHHMMSS,
formatDate,
subDay,
addDay,
call
}
挡板实现
存放挡板服务实现,请参照上面讲的案例或到github拉取。
imposters.ejs配置文件
{
"imposters": [
<% include ./test/predicates_inject.json %>,
<% include ./test/predicates.json %>,
<% include ./test/proxy.json %>,
<% include ./test/mockjs.json %>,
<% include ./test/responses_inject.json %>,
<% include ./test/_behaviors.json %>,
<% include ./test/shellTransform.json %>
]
}
start.sh linux启动脚本
利用node的npm start也是可以的,这里自己写的目的是把定时任务也包含在里面。
#停止服务
mb stop
#---------------------------定时任务 start-----------------------------
#先停止进程
ps -ef|grep testquartz |grep -v grep|cut -c 9-15|xargs kill -9
#再启动进程
node quartz/testquartz.js &
#---------------------------定时任务 end-----------------------------
#启动服务
mb start --configfile imposters.ejs --allowInjection >test.out 2>&1 &
stop.sh linux停止脚本
mb stop
#---------------------------定时任务 start-----------------------------
ps -ef|grep testquartz |grep -v grep|cut -c 9-15|xargs kill -9
#---------------------------定时任务 end-----------------------------
netstat -antp|grep 2525 |grep -v grep|cut -c 80-85|xargs kill -9
DEMO代码
总结
本文主要是先从当前的微服务架构说起,调用外围系统给开发、测试、演示的痛点,给出挡板的架构规划,然后利用mountebank的强大功能,通过脚本+实战的方式一步步详细说明。
不知读到最后的您有没收获? 用mountebank来做挡板其实很简单,但必须多实践才能更好的掌握其精髓,更好的进行适合自己业务的扩展。
系统测试利器之挡板实战(一)
系统测试利器之挡板实战(二)
系统测试利器之挡板实战(三)
系统测试利器之挡板实战(四)
系统测试利器之挡板实战(五)
系统测试利器之挡板实战(六)
网友评论