其他测试框架 STF 二次开发辛酸之路

0x88 · 发布于 2016年10月14日 · 最后由 0x88 回复于 2017年07月31日 · 3095 次阅读
本帖已被设为精华帖!

前言

接触STF平台已经有一段时间,但最近一个月多才正式做二次开发,分享一些二次开发的经验。有人对STF二次开发不太看好,代码看不懂,表示学习成本太高,不愿意去学习。语言这东西嘛,多看几遍就看懂了。对于STF平台的整体架构个人认为是写得非常的好,逻辑清晰,不仅仅做二次开发,我觉得其web架构与设计模式都是值得去学习。当然很多人觉得宁愿想用java和php去重新写一个类STF平台(server和web),但是又有多少人能做得出来更别说优于它了?当然testin就不说了,他们是一家公司在做这套东西。唠叨就说这么多,现在分享一下STF二次开发的一些经验吧。

平台的一些介绍

1.平台语言
1)使用html使用的是jade模版,后来因为jade改名为pug,因此现在使用的是pug模版引擎。

2)前端使用的是angularjs 1.x版本,虽然2016年5月份左右,google推出了angularjs2.0版本,但官方并不想更新angularjs的版本,继续使用1.x版本。如果改成2.0版本,相当于要重写平台。如果是重写平台,官方可能不会使用angularjs。
3)后端使用的是nodejs

4)数据库使用的是对象型数据库rethinkdb。至于为什么使用这个数据库,官方的回答是:看到新出来的rethinkdb,想尝试一下新的东西,于是就采用了。

2.STF平台组成
STF平台是由:stf,adbkit,STFService.apk,stf-syrup,adbkit-logcat,stf-device-db,adbkit-apkreader,
minicap,minitouch,minirev等组件共同组成。

3.STF目录结构
/.tx transifex网站相关的配置,主要是用于其它语言翻译
/bin 启动文件,其实是链接到lib/cli.js
/docker Docker的相关配置
/lib 后端代码
/res 前端web的代码
/conf 应该是mysql的配置文件,但是目前没有使用,是个没用的配置
/doc 一些STF平台的说明
/test 检测平台的状态
/vendor 需要安装到手机上的应用或者服务
/node_modules npm相关的组件
/rethinkdb_data rethinkdb的数据库文件,建议在stf目录下执行rethinkdb
Package.json npm所需要安装的组件集,以json格式保存
.... 其它说明文件与打包文件

4.STF平台启动
先启动rethinkdb,再使用stf local启动平台,stf 后面可带参数,stf --help可查看帮助。
stf 命令其实是调用了stf目录下lib/cli.js这个文件执行的。
修改了后端代码/lib 需要使用命令 gulp clean。
每次启动都好麻烦,而我的记性又不好。于是这里我写了一个shell的启动脚本start.sh(在目录/var/stf/下面),可直接运行此脚本启动。
一般启动: ./start.sh,由于ldap涉及到公司内部的信息,我就不写了。自行百度google 其ldap启动方法

para=$1
gulp clean
killall -9 node
killall -9 rethinkdb
ipaddr=`ifconfig eth0|awk 'NR==2{print $2}'|cut -d : -f2`
rethinkdb  --bind $ipaddr &
 sleep 3
DATE=`date +%F`

if [ "$para" = "ldap" ];then
#  echo $para
stf local --public-ip=$ipaddr  -C --poorxy-port 80 --auth-type ldap --auth-options '[ldap配置]' >> /var/stf/log/$DATE.log 2>&1 &
else
echo "noldap"
stf local --public-ip=$ipaddr  -C --poorxy-port 80 >> /var/stf/log/$DATE.log 2>&1 &
fi
sleep 6 

非ldap启动:直接./start.sh
Ldap启动:./start.sh ldap

5.关于rethinkdb
Rethinkdb 属于对象型数据库,使用的是json字符串格式保存数据(不建议内嵌json超过三层)。其操作有异于关系型数据,但其扩展性比关系型数据库好,可随时在数据表中插入数据,不需要做数据关联操作。

6.modules组件
Node相关的modules组件使用的是npm的安装方式,而Package.json是所要安装的组件名及其版本号,格式如下图所示。如需要新增组件也可以直接使用npm install 组件名@版本号。如果安装不成功,大多数是因为网络问题,建议开启VPN进行下载。

Openstf相关的adbkit等组件有源代码可以修改,底层操作手机也是用adbkit这些组件来做的,详细的语法及使用可直接进入相关的github官网查看。

前端开发

res目录结构
/app 前端web代码
/auth 登录认证方式
/common 语言翻译相关
/test 登录的跳转与帮助等
/web_modules web样式

res 目录分为service与web,service主要用于后端通讯操作,而web是前端展示。
Service目录在res/app/component/stf目录下。
比如需要增加一个task(任务管理的界面),则新建一个task文件件,里面创建几个文件:
index.js
task-controller.js
task.pug等
修改app目录下的app.js,增加require(‘./task’).name,如下图所示

增加task的service到目录stf/res/app/components/stf/下增加目录task文件夹,并创建index.js与taskservice.js,用于通讯。
一般都是使用get,post请求,而stf使用的是oboe模块来接收发送的。代码如下

var _ = require('lodash')
var oboe = require('oboe')

module.exports = function TaskServiceFactory($http, socket) {
  var TaskService = {}
  TaskService.reports = new Array()
   oboe('后端定义的api')
   .node('reports.*',function(reports){
        TaskService.reports.push(reports)})
  return TaskService
}

后端

db 是数据操作文件,分为api.js,index.js,setup.js,tables.js
db 的操作只需要修改tables.js与api.js即可,其它两个文件可以不用修改。
api.js主要用于数据库增删查操作

dbapi.Name = function(input){
  return db.run(查询语句)
} 

units是核心代码,根据其命名可得知其作用。需要增加什么功能就直接新建文件夹,当然代码肯定要自己写。
其它功能自己写,这里就只讲如何插入log
STF平台已经写了一个方法来显示Log的,require 其自带的logger.js就可以打日志了

var log = logger.createLogger('xxx模块')
.....
.....
log.info('描述',变量)
log.debug('描述',变量)
.....

数据库Rethinkdb

官网:www.rethinkdb.com
一般网络是连接不上rethinkdb的官网的很多时候真的都打不开,跟我们国内的网络真恶心,需要开VPN。
学习rethinkdb没有捷径,百度google都没有用。只能上官网看英文,不懂上github中提issue,英文不好的就得用力啃了。
启动完rethinkdb后的可访问网址http://ip:8080webview的操作界面,可在web上面对数据库进行操作。,可进入

好了就写这么多,早晨的时间总是过得飞快,国庆过后的7个工作日,辛酸辛酸。

共收到 37 条回复
605

好文,写得好清晰。 有个问题请教下,你们有对 stf 在 webstorm 做断点调试吗?具体是怎么进行的呢?

我在我本地上配置一直不成功,用默认代码的话 child-process 在 fork 的时候子进程使用同一个调试端口,会引起端口冲突无法启动。改用 child-process-debug 后,其中一个依赖库报错不支持 --debug 参数。

5512

没在本地上配置过.....
我比较懒,不想搞docker,于是通过打印log.info或者log.bug后直接扔测试服务器看报什么错来调试的。你可以试试搭个docker来调试。

118 monkey 将本帖设为了精华贴 10月14日 10:31
118

加精理由。清晰,有效实践

6853

正在用其他语言重写中 哈哈

5512

#5楼 @codeskyblue 这开发量估计比你改stf的工作量大N倍呀!😫

6853

#7楼 @0x88 stf的代码我已经快看了两遍了,我不会全部重写的,远控控制肯定是要写的,zeromq先干掉

5462

#8楼 @codeskyblue zmq不好干掉啊,它是stf的主力😂

5462

rethinkdb已经倒闭了,不知道后面官方会不会换数据库

5512

#10楼 @blueshark 官方的说先观望,看谁去接盘rethinkdb;再考虑是否更换。不过我想等我把界面改好了后,我也会考虑换mangodb。

6853

#9楼 @blueshark 可以干掉的,相信我,只是需要重写的东西多一些而已

114

梳理得很清楚!

12253

你好有什么办法取消掉登陆界面吗

5512

#14楼 @zangtian2 不建议把登录去掉,后面数据库逻辑与其它地方都会用到username和useremaiil,我这边的做法是通过跨域提供http接口,等我这边把事情做完后续会现发一个帖子。

96

多个设备从device切换到control界面,手机会自动打“123456”等字符到search bar里,这个谁遇到过啊?

5512

#16楼 @onlinesen123 你是个测试人员?要会问问题,你这啥都没有,谁能帮得了你?

96

#17楼 @0x88
感谢回复,
我昨天才安装的,发现这个问题,也没搞懂如何看log之类的,抱歉了。我贴个图。

就是从device界面切换到control界面,设备的searchbar上,会自动打入几个字符“123456”,而且切一次就打几个进去。。

5512

#18楼 @onlinesen123 部署的系统是什么??手机型号?有没有试下换台手机?据我了解新的手机都不会出现这种问题,是否是因为你的手机安装了什么软件?

96

#19楼 @0x88 果然,换我自己的手机就好了,公司手机都这样。。晕死。。
我再ubuntu14.04上,手机是7.0,软件基本就是google原生的。。估计是stf切换的时候发送了什么命令,导致intent启动输入了字符。。这个我会自己研究下。

另外请问下,你做2次开发,能否把项目放到github上面,我们一起来瞻仰下啊,这样大家一起来做,或许能把stf做完善点。

5512

#20楼 @onlinesen123 我只改lib/然后把http接口化,web端是找另外的开发做的,而且这是公司的项目,知识产权的问题,不适合公开源码。

96

#21楼 @0x88 大神,再请教下,我现在想加一个权限,好让我后台可以踢掉任何一个占用设备的账号。
我看了下这段disconnect的代码,调用api.user.deleteUserDeviceBySerial,需要auth_token,这个我后台拿不到啊,拿如何能直接踢掉用户占用的设备呢?请指教下吧,谢谢了。。。

var AUTH_TOKEN = '03f5e019a2f94a35b90c30e40829395b5a0d0f0e7fd14bc496a176b03e229540';

var client = new Swagger({
url: SWAGGER_URL
, usePromise: true
, authorizations: {
accessTokenAuth: new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + AUTH_TOKEN, 'header')
}
})

5512

#22楼 @onlinesen123 请不要叫我大神,我只是个Tester。
你太执著于改STF的代码了,我觉得你得换个思维方式。无论代码如何改,最终于的结果是,web-->中间一大串逻辑或者操作-->写数据库。
你可以试一下用数据库操作把设备占用的用户干掉。应该是devices表中的owner。

96

#23楼 @0x88 我试过了,干掉以后,device显示是可用状态,但实际谁都用不了,点击设备进去,没有任何显示。而且貌似之前的用户刷新下,虽然不再显示任何图像,但感觉还是owner的状态。

5512

#24楼 @onlinesen123😅 我只能发以下图片给你看看。用下代码对比工具嘛。

96

#25楼 @0x88 HI,再请教下,貌似看到一个功能,provider设备。我的需求是再随便一台PC上插入设备,stf能获取到这个PC地址下的设备吗?参数能否提供下,我试了多个参数都没成功

5512

你的意思是所有的电脑都装上STF然后每台接上手机都能连接到一个web界面用?

5462

#22楼 @onlinesen123 想踢掉任何一个占用设备的账号,告诉你一个后门,在stf界面up up down down left right left right enter就可以开启admin模式,可以踢掉任何用户。up是方向键上,down是下,enter是回车

5512

#28楼 @blueshark 这是啥操作呀。

5462

#29楼 @0x88 STF一个后门,可以打开root模式

8706

#29楼 @0x88 stf 采用ldap进行验证的话, "--ldap-search-field", "mail" 这个是用户名吧,跳转的验证页面有用户名和密码两个输入框,密码应该是什么呢?

5512

#31楼 @huafeihua 我不知道你的密码是什么,密码是LDAP服务器上面的用户对应的。

8706

#32楼 @0x88 ldap服务器我是自己搭的,一个的openladp,一直搞不懂用户名和密码各自对应的是ladp中的那个属性,"--ldap-search-field", "mail",这样等于把用户名关联到mail上了,那么密码应该是哪个属性呢,userPassword吗,我试了,这个连接失败了.

5512

#33楼 @huafeihua 我没搭过LDAP服务,对LDAP这一块不懂,这个是LDAP服务是公司的,参数也是IT组给我的,直接拿来用的。现在我都已经不用LDAP,重新写了一个SSO认证,所以这一块我也不会去学习了。

544eb5
55120x88 回复

看到了stf支持oauth2.0,想请教一下博主SSO认证的实现思路,最近刚刚入stf

5512
544eb5Furong 回复

看不懂STF的auth2.0的用法,我是直接把mock认证改成sso,sso无非是个跳转而已。

544eb5
55120x88 回复

博主能不能把你的sso实现过程说明的更详细一点啊

5512
544eb5Furong 回复

把代码发给你看如果你不懂SSO也没用,其实SSO没那么复杂,我这里时间比较宽裕,我记得好像就用了一天看了一下SSO,一天看了下STF平台,然后用了两天时间改了一下,然后用了一天测试了一下。先了解SSO就行了,把跳转改一下。好久之前写的代码,虽然是我写的,但我记性不好,至于怎么实现,什么是SSO,别问我为什么,因为我忘了。
PS:大小括号是不全的,所以只供你参考。

app.get('/', function(req, res) {
   res.redirect('/auth/mock/')
})

app.get('/auth/mock/',function(req,res){
if(!req.cookies.admin_key){
  res.status(200)
        .json({
          success: false
          , redirect: 'http://sso.oa.com/Index/login/appid/1111/'
        })
  }
else{
 var urlpath='/api?do=getInfo&appid='+1111+'&uid='
  +req.cookies.admin_uid+'&key='+encodeURIComponent(req.cookies.admin_key)
  log.info('urlpath:',urlpath)
  var opts = {
      host: 'sso.oa.com',
      port: 8888,
      path: urlpath,
      method: 'GET',
      headers: {
          'Content-Type': 'application/json'
      }
  }
  log.info('opts',opts)
var req = http.request(opts,function(res){
  res.setEncoding('utf8')
  res.on('data',function(chunk){
    log.info('aaaa',chunk)
    ssoreturn(chunk)
  })
})
req.end()

function ssoreturn(chunk){
  var chunkjson=JSON.parse(chunk)
  var token = jwtutil.encode({
      payload: {
        email: chunkjson.email
      , name: chunkjson.username
      }
    , secret: options.secret
    })
  log.info('chunk',chunkjson)
  log.info('chunk ret',chunkjson.ret)
    if(chunkjson.ret == 1){
      res.status(200)
        .json({
          success: true
          , user_name: chunkjson.username
          , redirect: urlutil.addParams(options.appUrl, {
                jwt: token
              })
          })
    }
    else{
      log.info('redirect.......')
      res.status(200)
        .json({
          success: false
          , user_name: chunkjson.username
          , redirect: 'sso.oa.com'
        })
    }
  }

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