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

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

作者: 老杜杂谈 | 来源:发表于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