STF STF 初步定制测试平台

syl7752 · 2016年08月26日 · 最后由 stronger 回复于 2019年06月14日 · 126 次阅读
本帖已被设为精华帖!

一个月前看了 monkey 大神的 stf 修改帖子后心生想法, 能不能根据 STF 定制一个符合我司测试需求的平台, 在上面跑一些自动化测试以及查看结果.

怀着这个想法, 啃了一个月的源码. 从最初的摸不着头脑到现在的略有领悟, 终于改造了一个简单的测试平台.

把经验和看法分享给大家. 第一次发帖, 有不足的地方还望大家指正,能多多交流


准备工作

关于 STF 环境搭建和运行我就不多说了, 可参考帖子:

https://testerhome.com/topics/2988

在这简单介绍下 stf 的结构,因为我刚开始接触就很摸不着头脑, 希望能对刚接触 STF 的人一些帮助吧

STF 代码主要有两个目录,lib 和 res. lib 里放的是后端的代码, res 里放的是前端的代码.

STF 启动运行命令是 stf local, 实际上是 node lib/cli.js local.
也就是说 lib 下 cli.js 是 stf 后台的入口,local 是传入的参数. cli.js 中启动了一些后端的服务

以下是启动 app 服务代码:

// app
, procutil.fork(__filename, [
  'app'
  , '--port', options.appPort
  , '--secret', options.authSecret
  , '--auth-url', options.authUrl || util.format(
    'http://%s:%d/auth/%s/'

启动方式就是调用了 procutil 的 fork(),fork 里是启动了一个 child_process,在这就不细说了.


界面改造

首先我想在页面最上方增加一个 test 的标签, 在 res/app/menu/menu.jade 里添加:

a(ng-href='/#!/devices', accesskey='1')
  span.fa.fa-sitemap
  span(ng-if='!$root.basicMode', translate) Devices
a(ng-href='/#!/apptest')
  span.fa.fa-bug
  span(ng-if='!$root.basicMode', translate) Test

增加了一个和 Devices 一样的标签, 修改了其中的图标为 fa-bug, 其实 STF 大多数的 icon 都是借用的 font awesome 里的 icon, 传送门:

http://fontawesome.io/

在 res/app 目录下新建 apptest 文件夹,

在 index 中配置 route:

.config(function($routeProvider) {
  $routeProvider.when('/apptest', {
    template: require('./app-test.jade')
  })
})

标签添加完成,在 test 标签下, 我想做两个子页面, 一个用于配置测试的, 一个展示测试状态列表,在 app-test.jade 中添加

div(fa-pane,pane-id='test-tabs')
  .widget-container.fluid-height
nice-tabs(key='ControlBottomTabs', tabs='appTestTabs', filter='')
div(fa-pane, pane-anchor='south', pane-size='50%', pane-handle='4').pane-bottom-p
  .widget-container.fluid-height
status-Directive(tests='tests',columns='columns')

为配置测试页增加 crawl 和 smoke 两个 tab:

$scope.appTestTabs = [
{
  title: gettext('Crawl'),
  icon: 'fa-refresh fa-fw',
  templateUrl: 'apptest/crawl/crawl.jade'
},
{
  title: gettext('Smoke'),
  icon: 'fa-rss fa-fw',
}
]

效果如下图:

statusDirective 是状态列表的 directive, 之所以放在 app-test.jade 里,是想切换 tab 时,状态列表始终展示.

在 directive 中可以进行适时的数据更新,该处是仿照 device 列表写的,

function createRow(test) {
  var id = test.id
  var tr = document.createElement('tr')
  var td

  tr.id = id

  for (var i = 0, l = columns.length; i < l; ++i) {
    td = scope.testCell[columns[i].name].build()
    scope.testCell[columns[i].name].update(td, test)
    tr.appendChild(td)
  }
  return tr
}

function updateRow(test)
{
  var tr = tbody.children[test.id]
  if (tr) {
    for (var i = 0, l = columns.length; i < l; ++i) {
  scope.testCell[columns[i].name].update(tr.cells[i], test) 
  }
  return tr
}

效果如下图:


数据存储

STF 使用的数据库是 rethinkdb, rethinkdb 是以 json 方式存储数据的. 先在 lib/db/tables.js 中新建表 tests 主键为 id:

tests: {
  primaryKey: 'id'
}

在 api.js 中定义操作数据库的方法

dbapi.saveTests = function(tests)
{
  var data = {
      testType: tests.testType
    , startTime: tests.startTime
    , endTime: tests.endTime
    , owner: tests.owner
    , serial: tests.serial
    , status: tests.status
    , result: tests.result
    , device: tests.device
  }
  return db.run(r.table('tests').get(tests.id).update(data))
    .then(function(stats) {
      if (stats.skipped) {
        data.id = tests.id
        data.createdAt = r.now()
        return db.run(r.table('tests').insert(data))
      }
    return stats
   })
}

dbapi.loadTest = function(id) 
{
  return db.run(r.table('tests').get(id))
}

dbapi.loadTests = function()
{
  return db.run(r.table('tests'))
}


前后端通信

有了界面, 要在后端执行我的遍历测试并返回结果,需要前后端通信.首先前端触发开始测试操作:

$scope.runTest = function()
{
  var mydevices = $scope.devices
  for (var i = 0; i < mydevices.length; i++) {
    var mydevice = mydevices[i]
    console.log(mydevice.model);
  if(mydevice.selected)
  {
    mydevice.testing = true;
    mydevice.tester = user;
    $scope.control = ControlService.create(mydevice, mydevice.channel)
    GroupService.invite(mydevice)
    $scope.control.runCrawler(mydevice)
  }
};
}

在这我给 device 新加了两个字段:测试状态 testing 和测试人员 tester, 因为在测试中的设备,我不想让它展示出来. tester 则在列表中展示

触发测试指令是用 websocket 来传达, 在 control-service 中添加

this.runCrawler = function(device)
{
  return sendOneWay('runCrawler',{
  device: device
})
}
this.stopTest = function(device,test)
{
  return sendOneWay('stopTest',{
  device: device
})
}

sendOneWay 里是向所有 client 发送 action,然后我们在后台中监听这个 action 在 lib/units/websocket/index.js 中添加:

.on('runCrawler', function(channel, data) {
  controller.runTest(io,data.device);
})
.on('stopTest', function(channel, data) {
  controller.stopTest(socket,data.serial);
})

controller 中是我们具体执行测试的代码:

this.runTest = function(socket,device){
  var myDevice = {};

  var test = createTest(device);

  crawler = spawn('java', ['-jar','monkeytest/monkeytest.jar','-d','android','-p',
    'com.android.text','-a','MainActivity','-s',device.serial,'-i',test.id]);
    console.log(device.serial);

  crawler.serial = device.serial
  crawler.stdout.on('data', (data) => {
    console.log(`stdout: ${data}`);
});

crawler.stderr.on('data', (data) => {
  console.log(`stderr: ${data}`);
});

crawler.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
  if(code == 0)
  {
    test.endTime = getNowFormatDate();
    test.status = 'Finish';
    dbapi.saveTests(test);
    dbapi.setDeviceTestStatus(crawler.serial,test.status)
    triggerTestComplete(socket,test);
  }
  else
  {
  test.endTime = getNowFormatDate();
  test.status = 'Stop';
  dbapi.saveTests(test);
  dbapi.setDeviceTestStatus(crawler.serial,test.status)
  triggerStopTest(socket,test);
  }
});
var serial = device.serial;
deviceList[serial] = crawler;
dbapi.saveTests(test);
dbapi.setDeviceTestStatus(device.serial,test.status)
  triggerStartTest(socket,test);
}
this.stopTest = function(socket,serial)
{
  deviceList[serial].kill();
}

由于执行的是我本地上的遍历工具, 所以用了 child_process, 在 close 时对 exitcode 进行判断,来分辨测试是正常执行完或中断. 然后对数据库操作后 trigger 前端更新状态

function triggerStartTest(socket,test)
{
  console.log('triggerStartTest');
  socket.emit('test.start',test);
}

在这也是用的 socket, 就不细述了.

效果图:


总结一下吧 STF 二次开发还是有些难度的, 像我这种只会 java 的刚开始看想死的心都有了.

踏遍了无数的坑, 辛酸只有自己知道. 不过风雨之后见彩虹, 看到有些成果心里也满足了.

希望有改造 STF 想法的同学也不要气馁,努力必有收获!

共收到 28 条回复 时间 点赞

看完此贴,我终于下定决心,重写 stf

给力,点赞,我也去下过来看看

—— 来自 TesterHome 官方 安卓客户端

思寒_seveniruby 将本帖设为了精华贴 08月27日 05:34

加精理由: 细节的讲解了用 nodejs 改造 stf 的细节. monkey 的分享带来了你的分享. 技术就是这样的流转和升华. 这符合我们社区的分享精神.

#4 楼 @seveniruby 感谢思寒 说得太好了 技术有交流分享才会有进步和升华

这篇太棒了,期待进一步的改造~~~

其实我在整理一些代码,看看能不能开源出来。太乱了现在

#8 楼 @monkey 恩 我代码也很乱 主要是速成 调试的东西太多了 很期待大神的开源~😀

有没有 rethinkdb 相关的分享?rethinkdb 操作方法虽然官方有文档,但实际用起来还是很多不懂。

#10 楼 @0x88 rethinkdb 资料太少了 我直接看官方文档也没头绪, 建议还是先从源码入手吧 带着问题看文档好些

厉害啊,我看了很长时间源码,一直没有特别清楚的头绪,对 nodejs 也不熟悉,最近还在研究这个东西

#12 楼 @blueshark 看些 angularjs 和 express 的资料 有问题多 google 吧 加油

#5 楼 @syl7752 虽然工程浩大,但可以先实现一小步,到今天我差不多重写了十分之一了

#14 楼 @codeskyblue 慢慢来~ 上手了就快了

为什么作者不附上源码呢

匿名 #18 · 2016年11月08日

#15 楼 @syl7752 做改造需要哪些准备呢?也想做一个类似的改造

#18 楼 @xus 建议去看下 angularjs,express,jade 的相关知识,在看源码的同时,带着问题去学习.

正在学习 stf 的二次开发,难度确实很大,就连安装 stf 都费了老大的劲。
大家做 stf 的有建群吗?可以及时讨论和支持什么的。

@syl7752 请问你对应代码到底放在哪些文件路径下面了?谢谢!

ftvbftvbq 回复

你指的是哪段代码

请问,界面修改那里,修改 gade 之后,应该执行什么命令才生效。

wangpengfei100 回复

前端修改刷新下就可以了

syl7752 回复

请问使用什么命令刷新,我是按照这个帖子安装的https://testerhome.com/topics/2988,,
好像没有 git 依赖到本地。看大家说 gulp,提示 “no gulpfile”

syl7752 回复

应该是说,apptest.css 里面的内容好像没有贴。实现不了作者的效果

能把完整的包给看下吗 谢谢

作者好牛,求源码,实在改不出作者的效果,非常感谢

求开源出来代码,避免重复造轮子啊

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