移动测试基础 从 0 构建自动化测试平台 (四) 文件上传与任务提交

K米测试 · 2017年05月06日 · 最后由 codeskyblue 回复于 2017年05月07日 · 2138 次阅读

关于网友推荐更好的方法

在第三篇中,关于前端页面的设计,陆续有网友给出了意见,其中有推荐一个工具:Pingendo,适合自主开发页面,有兴趣的网友可以自行体验下。

云测试平台要达到的效果

和大多数的云测试平台一样,我们希望实现的效果是:将被测 apk、ipa(甚至测试用例) 上传,后台执行测试,然后在前端任务列表中查看对应的测试结果。这一过程大致经历 8 个过程:
1、被测文件上传
2、将文件路径传递给后端 python
3、python 根据路径和参数执行测试
4、python 保存测试结果到数据库 (如: rethinkdb)
5、前端发起查看测试报告的请求
6、Node.js 从数据库中获取测试结果
7、Node.js 将测试结果传递给前端
8、前端展示测试结果

我会逐一和大家介绍每一过程的具体实现:
文件上传
先上效果图:

前端实现

定义一个 input 输入框,我使用了第三方库 dropzone 实现拖拽,通过添加 action 来定义文件上传后要执行的动作,代码如下:

input#dropfile.dropzone(action='/file_upload', name='filename', readonly='readonly', placeholder='将电脑里的文件拖拽到此上传,小于100M的APK文件')

Node.js 实现

在 app.js 中处理 file_upload 的 post 请求,代码如下:

/* 处理文件上传和保存 */
app.post('/file_upload', function (req, res) {
    log.info('文件上传:' + req.files);
    upload_file.save_app(req, res);
});

save_app 的实现

读取前端传递过来的文件,然后写到指定目录 uploads 下,代码如下:

exports.save_app = function(req, res){
    var des_file = path.join(__dirname, '../../uploads', req.files[0].originalname);
    fs.readFile(req.files[0].path, function(err, data){  
        fs.writeFile(des_file, data, function(err){  
            if(err){
                var response = {
                    errcode:50004,
                    errmsg: err
                };
                log.info( response );
                res.send(JSON.stringify(response));
            }else{
                var response = {
                    errcode:0,
                    filename:req.files[0].originalname
                };
                log.info( response );
                res.send(JSON.stringify(response));
            }
        });
    });
};

这样就完成了文件的上传。

提交任务,移交到后端 python 执行测试

创建一个任务,除了提交上传文件 (被测 app),可能还有其他参数,我们将所有参数写到表单 form 中。

前端实现

form#Step-Two-Div.Step-two(role='form', action='/upload/success', method='post')
//提交任务的按钮类型为submit,此处还定义了一个变量type,值为:compatibility
button.Blue-button(type='submit', name='type', value='compatibility ')
    | 提交任务

说明:提交任务时,会执行表单中的 action,即发起 /upload/success 请求,这里定义一个变量 type 是为了区分测试类型 (如兼容性、稳定性),不同的任务都提交到/upload/success, Node.js 端处理 upload/succes 的请求时通过判断 type 来区分测试类型,以便后端根据测试类型执行不同的 python 脚本。

Node.js 实现

在 app.js 中处理/upload/succes 的 post 请求,代码如下:

/* 处理提交参数 */
app.post('/upload/success', function (req, res) {
    log.info(req.body);
    var args = '';
    var type = '';

    if (req.body.filename === '' || req.body.filename === undefined) {
        var response = {
            errcode: 50001,
            errmsg: '请上传文件'
        };
        log.info(response);
        res.send(JSON.stringify(response));
        return;
    }

    if (req.body.email === '') {
        var response = {
            errcode: 50002,
            errmsg: '请填写邮箱地址'
        };
        log.info(response);
        res.send(JSON.stringify(response));
        return;
    } else {
        args += ' --email=' + req.body.email;
    }

    if (req.body.type !== undefined) {
        type = req.body.type;
        args += ' --test-type=' + type;
    }

    if (req.body.dev !== undefined) {
        args += ' --dev=' + req.body.dev;
    }

    if (req.body.monkey !== undefined) {
        args += ' --monkey=' + req.body.monkey;
    }

    apppath = path.join(__dirname, 'uploads', req.body.filename);

    fs.exists(apppath, function (exists) {
        if (!exists) {
            var response = {
                errcode: 50003,
                errmsg: apppath + ' 文件不存在'
            };
            log.info(response);
            res.send(JSON.stringify(response));
            return;
        } else {
            args += ' --app-path=' + apppath;
            log.info('args = ' + args);
            if (type == 'feedback' || type == 'versiontest'){
                args = apppath; // 数据分析模块只需要传递文件路径参数即可
                child_services.startAnalysis(type, args);
            }
            else {
                cmdqueue.inqueue(args);
            }

            rdb.findAll(function (tasks) {
                res.render('testmanager', {
                    "task_list": tasks
                })
            });

        }
    });
});

在这个方法里面,获取前端传递过来的文件路径 req.body.filename、邮箱 req.body.email、测试类型 req.body.type,同时将这些参数放在 args 中,我们将 args 作为参数传递给 child_process,child_process 在调用 python 脚本时,将 args 传递给 python。child_process 中调用 python 脚本的代码如下:

var cmd = 'python devices.py' + args;
log.info('当前任务为:%s', cmd);
var child = child_process.exec(cmd, options);

cmd 范例如下:

python vendor/comptest/devices.py --app-path=/home/K米4.6.6.apk --email=xiongjinfei@star-net.cn --test-type=compatibility

今天就暂时先写到这里,后续章节会持续更新,敬请期待……

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 2 条回复 时间 点赞

Dropzone 我喜欢

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册