美文网首页
系统测试利器之挡板实战(七)

系统测试利器之挡板实战(七)

作者: 老杜杂谈 | 来源:发表于2020-03-10 21:01 被阅读0次

挡板实战

上面也算是详细介绍了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代码

完整的代码demo已经传到Git,在这里。

总结

本文主要是先从当前的微服务架构说起,调用外围系统给开发、测试、演示的痛点,给出挡板的架构规划,然后利用mountebank的强大功能,通过脚本+实战的方式一步步详细说明。

不知读到最后的您有没收获? 用mountebank来做挡板其实很简单,但必须多实践才能更好的掌握其精髓,更好的进行适合自己业务的扩展。

系统测试利器之挡板实战(一)
系统测试利器之挡板实战(二)
系统测试利器之挡板实战(三)
系统测试利器之挡板实战(四)
系统测试利器之挡板实战(五)
系统测试利器之挡板实战(六)

相关文章

网友评论

      本文标题:系统测试利器之挡板实战(七)

      本文链接:https://www.haomeiwen.com/subject/yftndhtx.html