jenkins:集成sonar代码扫描+发送邮件

前提:

Jenkins

JDK

目录:

1、安装sonar插件:SonarQube Scanner for Jenkins

2、安装SonarQube

3、安装sonar-scanner

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

1.安装sonar插件:SonarQube Scanner for Jenkins

2、安装SonarQube,并配置mysql数据库

2.1 下载

下载地址:https://www.sonarqube.org/downloads/

  2.2 解压

下载后放在/usr/local目录下,并解压

  1. unzip sonarqube7.4.zip

 2.3 配置环境变量

  1. vi /etc/profile

里面写如下

  1. #set sonarqube
  2. export SONAR_HOME=/usr/local/sonarqube7.4
  3. PATH=$PATH:$SONAR_HOME/bin

使配置生效

  1. source /etc/profile

 2.4 启动并测试

进入到启动目录

  1. /usr/local/sonarqube7.4/bin/linuxx8664

执行启动命令

  1. ./sonar.sh start

启动时要查看es.log和sonar.log日志,还有web.log,关于数据库配置的错误会在此处显示

  1. tail f /usr/local/sonarqube6.7.6/logs/es.log
  1. tail f /usr/local/sonarqube6.7.6/logs/sonar.log
  1. tail f /usr/local/sonarqube6.7./logs/web.log

用root启动会报错,请参考我的另一篇文章:https://www.cnblogs.com/gcgc/p/10239590.html

所有问题解决完后启动,并测试,访问ip+port,OK了

如果默认的9000端口被占用了,则修改默认端口 vi /usr/local/sonarqube-7.4/conf/sonar.properties

注意sonar中还集成了elastic search默认端口为9001,所以要查看9001否也被占用lsof -i:9001,如果被占用,也是修改上面的文件

  2.5 sonar配置mysql数据库

sonar自带测试数据库,但是具体应用还是的换其他数据,这里我们使用mysql,

创建sonar数据库

  1. DROP DATABASE sonar;
  2.  
  3. CREATE DATABASE sonar CHARACTER SET utf8 COLLATE utf8_general_ci;
  4.  
  5. CREATE USER ‘sonar’ IDENTIFIED BY ‘sonar’;
  6.  
  7. GRANT ALL ON sonar.* TO ‘sonar’@‘%’ IDENTIFIED BY ‘sonar’;
  8.  
  9. GRANT ALL ON sonar.* TO ‘sonar’@‘localhost’ IDENTIFIED BY ‘sonar’;
  10.  
  11. FLUSH PRIVILEGES;

2.6 修改配置文件

修改vi /usr/local/sonarqube-7.4/conf/sonar.properties文件,如下的蓝色加粗字体

  1. # Property values can:
  2. # – reference an environment variable, for example sonar.jdbc.url= ${env:SONAR_JDBC_URL}
  3. # – be encrypted. See https://redirect.sonarsource.com/doc/settings-encryption.html
  4.  
  5. #————————————————————————————————–
  6. # DATABASE
  7. #
  8. # IMPORTANT:
  9. # – The embedded H2 database is used by default. It is recommended for tests but not for
  10. # production use. Supported databases are MySQL, Oracle, PostgreSQL and Microsoft SQLServer.
  11. # – Changes to database connection URL (sonar.jdbc.url) can affect SonarSource licensed products.
  12.  
  13. # User credentials.
  14. # Permissions to create tables, indices and triggers must be granted to JDBC user.
  15. # The schema must be created first.
  16. #sonar.jdbc.username=
  17. #sonar.jdbc.password=
  18. sonar.jdbc.username=sonar
  19. sonar.jdbc.password=sonar
  20. sonar.sorceEncoding=UTF8
  21. sonar.login=admin
  22. sonar.password=admin
  23.  
  24. #—– Embedded Database (default)
  25. # H2 embedded database server listening port, defaults to
  26. #sonar.embeddedDatabase.port=
  27.  
  28. #—– DEPRECATED
  29. #—– MySQL >=5.6 && <8.0
  30. # Support of MySQL is dropped in Data Center Editions and deprecated in all other editions
  31. # Only InnoDB storage engine is supported (not myISAM).
  32. # Only the bundled driver is supported. It can not be changed.
  33. sonar.jdbc.url=jdbc:mysql://192.168.207.160:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false
  34.  
  35. #—– Oracle 11g/12c
  36. # The Oracle JDBC driver must be copied into the directory extensions/jdbc-driver/oracle/.
  37. # Only the thin client is supported, and only the versions 11.2.x or 12.2.x must be used. See
  38. # https://jira.sonarsource.com/browse/SONAR-9758 for more details.
  39. # If you need to set the schema, please refer to http://jira.sonarsource.com/browse/SONAR-5000
  40. #sonar.jdbc.url=jdbc:oracle:thin:@localhost:/XE
  41.  
  42. #—– PostgreSQL 9.3 or greater
  43. # By default the schema named “public” is used. It can be overridden with the parameter “currentSchema”.
  44. #sonar.jdbc.url=jdbc:postgresql://localhost/sonarqube?currentSchema=my_schema
  45.  
  46. #—– Microsoft SQLServer / and SQL Azure
  47. # A database named sonar must exist and its collation must be case-sensitive (CS) and accent-sensitive (AS)
  48. # Use the following connection string if you want to use integrated security with Microsoft Sql Server
  49. # Do not set sonar.jdbc.username or sonar.jdbc.password property if you are using Integrated Security
  50. # For Integrated Security to work, you have to download the Microsoft SQL JDBC driver package from
  51. # https://www.microsoft.com/en-us/download/details.aspx?id=55539
  52. # and copy sqljdbc_auth.dll to your path. You have to copy the bit or bit version of the dll
  53. # depending upon the architecture of your server machine.
  54. #sonar.jdbc.url=jdbc:sqlserver://localhost;databaseName=sonar;integratedSecurity=true
  55.  
  56. # Use the following connection string if you want to use SQL Auth while connecting to MS Sql Server.
  57. # Set the sonar.jdbc.username and sonar.jdbc.password appropriately.
  58. #sonar.jdbc.url=jdbc:sqlserver://localhost;databaseName=sonar
  59.  
  60. #—– Connection pool settings
  61. # The maximum number of active connections that can be allocated
  62. # at the same time, or negative for no limit.
  63. # The recommended value is 1.2 * max sizes of HTTP pools. For example if HTTP ports are
  64. # enabled with default sizes (, see property sonar.web.http.maxThreads)
  65. # then sonar.jdbc.maxActive should be 1.2 * = .
  66. #sonar.jdbc.maxActive=
  67.  
  68. # The maximum number of connections that can remain idle in the
  69. # pool, without extra ones being released, or negative for no limit.
  70. #sonar.jdbc.maxIdle=
  71.  
  72. # The minimum number of connections that can remain idle in the pool,
  73. # without extra ones being created, or zero to create none.
  74. #sonar.jdbc.minIdle=
  75.  
  76. # The maximum number of milliseconds that the pool will wait (when there
  77. # are no available connections) for a connection to be returned before
  78. # throwing an exception, or <= to wait indefinitely.
  79. #sonar.jdbc.maxWait=
  80.  
  81. #sonar.jdbc.minEvictableIdleTimeMillis=
  82. #sonar.jdbc.timeBetweenEvictionRunsMillis=
  83.  
  84. #————————————————————————————————–
  85. # WEB SERVER
  86. # Web server is executed in a dedicated Java process. By default heap size is 512Mb.
  87. # Use the following property to customize JVM options.
  88. # Recommendations:
  89. #
  90. # The HotSpot Server VM is recommended. The property -server should be added if server mode
  91. # is not enabled by default on your environment:
  92. # http://docs.oracle.com/javase/8/docs/technotes/guides/vm/server-class.html
  93. #
  94. # Startup can be long if entropy source is short of entropy. Adding
  95. # -Djava.security.egd=file:/dev/./urandom is an option to resolve the problem.
  96. # See https://wiki.apache.org/tomcat/HowTo/FasterStartUp#Entropy_Source
  97. #
  98. #sonar.web.javaOpts=-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError
  99.  
  100. # Same as previous property, but allows to not repeat all other settings like -Xmx
  101. #sonar.web.javaAdditionalOpts=
  102.  
  103. # Binding IP address. For servers with more than one IP address, this property specifies which
  104. # address will be used for listening on the specified ports.
  105. # By default, ports will be used on all IP addresses associated with the server.
  106. #sonar.web.host=0.0.0.0
  107.  
  108. # Web context. When set, it must start with forward slash (for example /sonarqube).
  109. # The default value is root context (empty value).
  110. #sonar.web.context=
  111. # TCP port for incoming HTTP connections. Default value is .
  112. sonar.web.port=9000
  113.  
  114. # The maximum number of connections that the server will accept and process at any given time.
  115. # When this number has been reached, the server will not accept any more connections until
  116. # the number of connections falls below this value. The operating system may still accept connections
  117. # based on the sonar.web.connections.acceptCount property. The default value is .
  118. #sonar.web.http.maxThreads=
  119.  
  120. # The minimum number of threads always kept running. The default value is .
  121. #sonar.web.http.minThreads=
  122.  
  123. # The maximum queue length for incoming connection requests when all possible request processing
  124. # threads are in use. Any requests received when the queue is full will be refused.
  125. # The default value is .
  126. #sonar.web.http.acceptCount=
  127.  
  128. # By default users are logged out and sessions closed when server is restarted.
  129. # If you prefer keeping user sessions open, a secret should be defined. Value is
  130. # HS256 key encoded with base64. It must be unique for each installation of SonarQube.
  131. # Example of command-line:
  132. # echo -n “type_what_you_want” | openssl dgst -sha256 -hmac “key” -binary | base64
  133. #sonar.auth.jwtBase64Hs256Secret=
  134.  
  135. # The inactivity timeout duration of user sessions, in minutes. After the configured
  136. # period of time, the user is logged out.
  137. # The default value is set to days ( minutes)
  138. # and cannot be greater than months. Value must be strictly positive.
  139. #sonar.web.sessionTimeoutInMinutes=
  140.  
  141. # A passcode can be defined to access some web services from monitoring
  142. # tools without having to use the credentials of a system administrator.
  143. # Check the Web API documentation to know which web services are supporting this authentication mode.
  144. # The passcode should be provided in HTTP requests with the header “X-Sonar-Passcode”.
  145. # By default feature is disabled.
  146. #sonar.web.systemPasscode=
  147.  
  148. #————————————————————————————————–
  149. # SSO AUTHENTICATION
  150.  
  151. # Enable authentication using HTTP headers
  152. #sonar.web.sso.enable=false
  153.  
  154. # Name of the header to get the user login.
  155. # Only alphanumeric, ‘.’ and ‘@’ characters are allowed
  156. #sonar.web.sso.loginHeader=X-Forwarded-Login
  157.  
  158. # Name of the header to get the user name
  159. #sonar.web.sso.nameHeader=X-Forwarded-Name
  160.  
  161. # Name of the header to get the user email (optional)
  162. #sonar.web.sso.emailHeader=X-Forwarded-Email
  163.  
  164. # Name of the header to get the list of user groups, separated by comma (optional).
  165. # If the sonar.sso.groupsHeader is set, the user will belong to those groups if groups exist in SonarQube.
  166. # If none of the provided groups exists in SonarQube, the user will only belong to the default group.
  167. # Note that the default group will always be set.
  168. #sonar.web.sso.groupsHeader=X-Forwarded-Groups
  169.  
  170. # Interval used to know when to refresh name, email and groups.
  171. # During this interval, if for instance the name of the user is changed in the header, it will only be updated after X minutes.
  172. #sonar.web.sso.refreshIntervalInMinutes=
  173.  
  174. #————————————————————————————————–
  175. # LDAP CONFIGURATION
  176.  
  177. # Enable the LDAP feature
  178. # sonar.security.realm=LDAP
  179.  
  180. # Set to true when connecting to a LDAP server using a case-insensitive setup.
  181. # sonar.authenticator.downcase=true
  182.  
  183. # URL of the LDAP server. Note that if you are using ldaps, then you should install the server certificate into the Java truststore.
  184. # ldap.url=ldap://localhost:10389
  185.  
  186. # Bind DN is the username of an LDAP user to connect (or bind) with. Leave this blank for anonymous access to the LDAP directory (optional)
  187. # ldap.bindDn=cn=sonar,ou=users,o=mycompany
  188.  
  189. # Bind Password is the password of the user to connect with. Leave this blank for anonymous access to the LDAP directory (optional)
  190. # ldap.bindPassword=secret
  191.  
  192. # Possible values: simple | CRAM-MD5 | DIGEST-MD5 | GSSAPI See http://java.sun.com/products/jndi/tutorial/ldap/security/auth.html (default: simple)
  193. # ldap.authentication=simple
  194.  
  195. # See :
  196. # * http://java.sun.com/products/jndi/tutorial/ldap/security/digest.html
  197. # * http://java.sun.com/products/jndi/tutorial/ldap/security/crammd5.html
  198. # (optional)
  199. # ldap.realm=example.org
  200.  
  201. # Context factory class (optional)
  202. # ldap.contextFactoryClass=com.sun.jndi.ldap.LdapCtxFactory
  203.  
  204. # Enable usage of StartTLS (default : false)
  205. # ldap.StartTLS=true
  206.  
  207. # Follow or not referrals. See http://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html (default: true)
  208. # ldap.followReferrals=false
  209.  
  210. # USER MAPPING
  211.  
  212. # Distinguished Name (DN) of the root node in LDAP from which to search for users (mandatory)
  213. # ldap.user.baseDn=cn=users,dc=example,dc=org
  214.  
  215. # LDAP user request. (default: (&(objectClass=inetOrgPerson)(uid={login})) )
  216. # ldap.user.request=(&(objectClass=user)(sAMAccountName={login}))
  217.  
  218. # Attribute in LDAP defining the user’s real name. (default: cn)
  219. # ldap.user.realNameAttribute=name
  220.  
  221. # Attribute in LDAP defining the user’s email. (default: mail)
  222. # ldap.user.emailAttribute=email
  223.  
  224. # GROUP MAPPING
  225.  
  226. # Distinguished Name (DN) of the root node in LDAP from which to search for groups. (optional, default: empty)
  227. # ldap.group.baseDn=cn=groups,dc=example,dc=org
  228.  
  229. # LDAP group request (default: (&(objectClass=groupOfUniqueNames)(uniqueMember={dn})) )
  230. # ldap.group.request=(&(objectClass=group)(member={dn}))
  231.  
  232. # Property used to specifiy the attribute to be used for returning the list of user groups in the compatibility mode. (default: cn)
  233. # ldap.group.idAttribute=sAMAccountName
  234.  
  235. #————————————————————————————————–
  236. # COMPUTE ENGINE
  237. # The Compute Engine is responsible for processing background tasks.
  238. # Compute Engine is executed in a dedicated Java process. Default heap size is 512Mb.
  239. # Use the following property to customize JVM options.
  240. # Recommendations:
  241. #
  242. # The HotSpot Server VM is recommended. The property -server should be added if server mode
  243. # is not enabled by default on your environment:
  244. # http://docs.oracle.com/javase/8/docs/technotes/guides/vm/server-class.html
  245. #
  246. #sonar.ce.javaOpts=-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError
  247.  
  248. # Same as previous property, but allows to not repeat all other settings like -Xmx
  249. #sonar.ce.javaAdditionalOpts=
  250.  
  251. #————————————————————————————————–
  252. # ELASTICSEARCH
  253. # Elasticsearch is used to facilitate fast and accurate information retrieval.
  254. # It is executed in a dedicated Java process. Default heap size is 512Mb.
  255. #
  256. # ————————————————–
  257. # Word of caution for Linux users on 64bits systems
  258. # ————————————————–
  259. # Please ensure Virtual Memory on your system is correctly configured for Elasticsearch to run properly
  260. # (see https://www.elastic.co/guide/en/elasticsearch/reference/5.5/vm-max-map-count.html for details).
  261. #
  262. # When SonarQube runs standalone, a warning such as the following may appear in logs/es.log:
  263. # “max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]”
  264. # When SonarQube runs as a cluster, however, Elasticsearch will refuse to start.
  265. #
  266.  
  267. # JVM options of Elasticsearch process
  268. #sonar.search.javaOpts=-Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError
  269.  
  270. # Same as previous property, but allows to not repeat all other settings like -Xmx
  271. #sonar.search.javaAdditionalOpts=
  272.  
  273. # Elasticsearch port. Default is . Use to get a free port.
  274. # As a security precaution, should be blocked by a firewall and not exposed to the Internet.
  275. sonar.search.port=9002
  276.  
  277. # Elasticsearch host. The search server will bind this address and the search client will connect to it.
  278. # Default is loopback address.
  279. # As a security precaution, should NOT be set to a publicly available address.
  280. #sonar.search.host=
  281.  
  282. #————————————————————————————————–
  283. # UPDATE CENTER
  284.  
  285. # Update Center requires an internet connection to request https://update.sonarsource.org
  286. # It is enabled by default.
  287. #sonar.updatecenter.activate=true
  288.  
  289. # HTTP proxy (default none)
  290. #http.proxyHost=
  291. #http.proxyPort=
  292. # HTTPS proxy (defaults are values of http.proxyHost and http.proxyPort)
  293. #https.proxyHost=
  294. #https.proxyPort=
  295.  
  296. # NT domain name if NTLM proxy is used
  297. #http.auth.ntlm.domain=
  298.  
  299. # SOCKS proxy (default none)
  300. #socksProxyHost=
  301. #socksProxyPort=
  302.  
  303. # Proxy authentication (used for HTTP, HTTPS and SOCKS proxies)
  304. #http.proxyUser=
  305. #http.proxyPassword=
  306.  
  307. #————————————————————————————————–
  308. # LOGGING
  309.  
  310. # SonarQube produces logs in logs files located in the same directory (see property sonar.path.logs below),
  311. # one per process:
  312. # Main process (aka. App) logs in sonar.log
  313. # Web Server (aka. Web) logs in web.log
  314. # Compute Engine (aka. CE) logs in ce.log
  315. # Elasticsearch (aka. ES) logs in es.log
  316. #
  317. # All files follow the same rolling policy (see sonar.log.rollingPolicy and sonar.log.maxFiles) but it applies
  318. # individually (eg. if sonar.log.maxFiles=, there can be at most of each files, ie. files in total).
  319. #
  320. # All files have logs in the same format:
  321. #
  322. # |—————–| |—| |-|——————–||——————————| |——————————————————————————————————————————|
  323. # 2016.11. :: INFO ce[AVht0dNXFcyiYejytc3m][o.s.s.c.t.CeWorkerCallableImpl] Executed task | project=org.sonarqube:example-java-maven | type=REPORT | id=AVht0dNXFcyiYejytc3m | submitter=admin | time=1699ms
  324. #
  325. # : timestamp. Format is YYYY.MM.DD HH:MM:SS
  326. # YYYY: year on digits
  327. # MM: month on digits
  328. # DD: day on digits
  329. # HH: hour of day on digits in hours format
  330. # MM: minutes on digits
  331. # SS: seconds on digits
  332. # : log level.
  333. # Possible values (in order of descending criticality): ERROR, WARN, INFO, DEBUG and TRACE
  334. # : process identifier. Possible values: app (main), web (Web Server), ce (Compute Engine) and es (Elasticsearch)
  335. # : SQ thread identifier. Can be empty.
  336. # In the Web Server, if present, it will be the HTTP request ID.
  337. # In the Compute Engine, if present, it will be the task ID.
  338. # : logger name. Usually a class canonical name.
  339. # Package names are truncated to keep the whole field to characters max
  340. # : log payload. Content of this field does not follow any specific format, can vary in length and include line returns.
  341. # Some logs, however, will follow the convention to provide data in payload in the format ” | key=value”
  342. # Especially, log of profiled pieces of code will end with ” | time=XXXXms”.
  343.  
  344. # Global level of logs (applies to all processes).
  345. # Supported values are INFO (default), DEBUG and TRACE
  346. #sonar.log.level=INFO
  347.  
  348. # Level of logs of each process can be controlled individually with their respective properties.
  349. # When specified, they overwrite the level defined at global level.
  350. # Supported values are INFO, DEBUG and TRACE
  351. #sonar.log.level.app=INFO
  352. #sonar.log.level.web=INFO
  353. #sonar.log.level.ce=INFO
  354. #sonar.log.level.es=INFO
  355.  
  356. # Path to log files. Can be absolute or relative to installation directory.
  357. # Default is <installation home>/logs
  358. #sonar.path.logs=logs
  359.  
  360. # Rolling policy of log files
  361. # – based on time if value starts with “time:”, for example by day (“time:yyyy-MM-dd”)
  362. # or by month (“time:yyyy-MM”)
  363. # – based on size if value starts with “size:”, for example “size:10MB”
  364. # – disabled if value is “none”. That needs logs to be managed by an external system like logrotate.
  365. #sonar.log.rollingPolicy=time:yyyy-MM-dd
  366.  
  367. # Maximum number of files to keep if a rolling policy is enabled.
  368. # – maximum value is on size rolling policy
  369. # – unlimited on time rolling policy. Set to zero to disable old file purging.
  370. #sonar.log.maxFiles=
  371.  
  372. # Access log is the list of all the HTTP requests received by server. If enabled, it is stored
  373. # in the file {sonar.path.logs}/access.log. This file follows the same rolling policy as other log file
  374. # (see sonar.log.rollingPolicy and sonar.log.maxFiles).
  375. #sonar.web.accessLogs.enable=true
  376.  
  377. # Format of access log. It is ignored if sonar.web.accessLogs.enable=false. Possible values are:
  378. # – “common” is the Common Log Format, shortcut to: %h %l %u %user %date “%r” %s %b
  379. # – “combined” is another format widely recognized, shortcut to: %h %l %u [%t] “%r” %s %b “%i{Referer}” “%i{User-Agent}”
  380. # – else a custom pattern. See http://logback.qos.ch/manual/layouts.html#AccessPatternLayout.
  381. # The login of authenticated user is not implemented with “%u” but with “%reqAttribute{LOGIN}” (since version 6.1).
  382. # The value displayed for anonymous users is “-“.
  383. # The SonarQube’s HTTP request ID can be added to the pattern with “%reqAttribute{ID}” (since version 6.2).
  384. # If SonarQube is behind a reverse proxy, then the following value allows to display the correct remote IP address:
  385. #sonar.web.accessLogs.pattern=%i{X-Forwarded-For} %l %u [%t] “%r” %s %b “%i{Referer}” “%i{User-Agent}” “%reqAttribute{ID}”
  386. # Default value (which was “combined” before version 6.2) is equivalent to “combined + SQ HTTP request ID”:
  387. #sonar.web.accessLogs.pattern=%h %l %u [%t] “%r” %s %b “%i{Referer}” “%i{User-Agent}” “%reqAttribute{ID}”
  388.  
  389. #————————————————————————————————–
  390. # OTHERS
  391.  
  392. # Delay in seconds between processing of notification queue. Default is seconds.
  393. #sonar.notifications.delay=
  394.  
  395. # Paths to persistent data files (embedded database and search index) and temporary files.
  396. # Can be absolute or relative to installation directory.
  397. # Defaults are respectively <installation home>/data and <installation home>/temp
  398. #sonar.path.data=data
  399. #sonar.path.temp=temp
  400.  
  401. # Telemetry – Share anonymous SonarQube statistics
  402. # By sharing anonymous SonarQube statistics, you help us understand how SonarQube is used so we can improve the product to work even better for you.
  403. # We don’t collect source code or IP addresses. And we don’t share the data with anyone else.
  404. # To see an example of the data shared: login as a global administrator, call the WS api/system/info and check the Statistics field.
  405. #sonar.telemetry.enable=true
  406.  
  407. #————————————————————————————————–
  408. # DEVELOPMENT – only for developers
  409. # The following properties MUST NOT be used in production environments.
  410.  
  411. # Elasticsearch HTTP connector
  412. #sonar.search.httpPort=-

重新启动sonar,好了测试一下

3、安装sonar-scanner

3.1 下载

https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner

3.2 解压

下载后放在/usr/local目录下,并解压

  1. unzip sonarscannercli3.2.0.1227linux.zip

3.3 配置环境变量

和上面一样修改/etc/profile文件,修改完记得 source /etc/profile

  1. #set sonarqube
  2. export SONAR_HOME=/usr/local/sonarqube7.4
  3. export SONAR_SCANNER_HOME=/usr/local/sonarscanner3.2.0.1227linux
  4. PATH=$PATH:$SONAR_HOME/bin:$SONAR_SCANNER_HOME/bin

修改完记得 source /etc/profile

3.4 测试

进入/usr/local/sonar-scanner-3.2.0.1227-linux/bin执行:

  1. ./sonarscanner h

如果出现如下就说明安装好了

  1. [root@iZbp1bb2egi7w0ueys548qZ bin]# ./sonarscanner h
  2. INFO:
  3. INFO: usage: sonarscanner [options]
  4. INFO:
  5. INFO: Options:
  6. INFO: D,–define <arg> Define property
  7. INFO: h,–help Display help information
  8. INFO: v,–version Display version information
  9. INFO: X,–debug Produce execution debug output

4、在Jenkins中配置sonar

SonarScanner和SonarQube的关系类似于客户端与服务端,由于SonarScanner工具需要把扫描的代码及结果发送到SonarQube服务器上,所以需要配置SonarQube服务地址。

在Jenkins系统配置>>系统设中配置如下:

5、在Jenkins中配置sonar-scanner

全局工具配置中配置如下:

6、增加构建步骤

Analysis properties 内容如下

  1. sonar.projectKey=eauth
  2. sonar.projectName=电商用户服务-auth
  3. sonar.projectVersion=1.0
  4. sonar.sources=./
  5. sonar.language=java
  6. sonar.sourceEncoding=UTF
  7. sonar.java.binaries=./
  8. sonar.login=admin
  9. sonar.password=admin

7、构建一下

8、添加自定义邮件模板

在jenkins所在机器上/root/.jenkins目录下,新建/sonar_script文件夹,里面放如下脚本:sonar.py和table.html

sonar.py内容如下

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import pymysql,os,sys
  5. from jinja2 import FileSystemLoader,Environment
  6.  
  7. def select_project_uuid(project_name):
  8. db = pymysql.connect(host=“192.168.207.160”, port=3306, user=“sonar”, passwd=“sonar”, db=“sonar”)
  9. cursor = db.cursor()
  10. select_p_uuid=“SELECT project_uuid,kee FROM projects WHERE `name`= ‘%s'” %(project_name)
  11. cursor.execute(select_p_uuid)
  12. result = cursor.fetchone()
  13. p_uuid = result[0]
  14. projectKey = result[1]
  15. db.close()
  16. return(p_uuid, projectKey)
  17.  
  18. def select_total_info(p_uuid):
  19. total_info=[]
  20. # 使用cursor()方法获取操作游标
  21. db = pymysql.connect(host=“192.168.207.160”, port=3306, user=“sonar”, passwd=“sonar”, db=“sonar”)
  22. cursor = db.cursor()
  23.  
  24. select_p_links = “SELECT text_value FROM project_measures WHERE text_value LIKE ‘java=%’ and component_uuid=” + “\'” + p_uuid + “\'”
  25. cursor.execute(select_p_links)
  26. p_links = cursor.fetchone()[0].split(“=”)[1]
  27.  
  28. sql_info = “SELECT count(*) FROM issues WHERE project_uuid=’%s’ and issue_type =%s”
  29. for leak in [2,3,1]:
  30. search_data = sql_info %(p_uuid, leak)
  31. cursor.execute(search_data)
  32. total_info.append(cursor.fetchone()[0])
  33. db.close()
  34. return p_links,total_info
  35.  
  36. def select_bugs(p_uuid):
  37. bugs=[]
  38. db = pymysql.connect(host=“192.168.207.160”, port=3306, user=“sonar”, passwd=“sonar”, db=“sonar”)
  39. cursor = db.cursor()
  40.  
  41. sql_info = “SELECT count(*) FROM issues WHERE project_uuid=’%s’ and issue_type =2 AND severity =’%s'”
  42. for leak in [‘BLOCKER’,‘CRITICAL’,“MAJOR”,‘MINOR’,‘INFO’]:
  43. search_data=sql_info % (p_uuid,leak)
  44. cursor.execute(search_data)
  45. bugs.append(cursor.fetchone()[0])
  46. db.close()
  47. return bugs
  48.  
  49. def select_leaks(p_uuid):
  50. leaks=[]
  51. db = pymysql.connect(host=“192.168.207.160”, port=3306, user=“sonar”, passwd=“sonar”, db=“sonar”)
  52. cursor = db.cursor()
  53.  
  54. sql_info = “SELECT count(*) FROM issues WHERE project_uuid=’%s’ and issue_type =3 AND severity =’%s'”
  55. for leak in [‘BLOCKER’,‘CRITICAL’,“MAJOR”,‘MINOR’,‘INFO’]:
  56. search_data=sql_info % (p_uuid,leak)
  57. cursor.execute(search_data)
  58. leaks.append(cursor.fetchone()[0])
  59. db.close()
  60. return leaks
  61.  
  62. def select_bad_tastes(p_uuid):
  63. tastes=[]
  64. db = pymysql.connect(host=“192.168.207.160”, port=3306, user=“sonar”, passwd=“sonar”, db=“sonar”)
  65. cursor = db.cursor()
  66.  
  67. sql_info=“SELECT count(*) FROM issues WHERE project_uuid=’%s’ and issue_type =1 AND severity =’%s'”
  68. for leak in [‘BLOCKER’,‘CRITICAL’,“MAJOR”,‘MINOR’,‘INFO’]:
  69. search_data=sql_info % (p_uuid,leak)
  70. cursor.execute(search_data)
  71. tastes.append(cursor.fetchone()[0])
  72. return tastes
  73. db.close()
  74.  
  75. curpath = os.getcwd()
  76. table_tem_name=“table.html”
  77. def generate_errmsg_table(s_lines=“”, total_data=[], bugs=[],leaks=[],tastes=[],report_url=“”):
  78. env = Environment(loader=FileSystemLoader(curpath, ‘utf-8’)) # 创建一个包加载器对象
  79. template = env.get_template(table_tem_name)
  80. html_content = (template.render(lins=s_lines,total_data=total_data, bugs=bugs,leaks = leaks,tastes=tastes,report_url=report_url))
  81. fh = open(report_html_path, ‘w’)
  82. fh.write(html_content)
  83. fh.close()
  84.  
  85. project_name = sys.argv[1]
  86. report_html_path=“report\\”+project_name+“.html”
  87. p_uuid, projectKey=select_project_uuid(project_name)
  88. s_lines,total_data=select_total_info(p_uuid)
  89. bugs=select_bugs(p_uuid)
  90. leaks=select_leaks(p_uuid)
  91. tastes=select_bad_tastes(p_uuid)
  92. report_url=“http://192.168.207.140:9000/dashboard?id=%s” %(projectKey)
  93. generate_errmsg_table(s_lines,total_data,bugs,leaks,tastes,report_url)

 table.html内容:

  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3. <head>
  4. <meta charset=“GBK”>
  5. <body>
  6. <p style=fontweight:bold;>一、总体情况:</p>
  7. <ul>
  8. <li style=fontweight:bold;>整体运行情况:扫描代码行数:<span style=color:blue>{{lins}}</span>, bugs:<span style=color:red>{{total_data[0]}}</span>, 漏洞:<span style=color:red>{{total_data[1]}}</span>, 坏味道:<span style=color:red>{{total_data[2]}}</span></li>
  9. <li style=fontweight:bold;>URL地址:<a style=fontweight:bold; href={{report_url}} >{{report_url}}</a></li>
  10. </ul>
  11. <p style=fontweight:bold;>二、错误信息详情:</p>
  12. <table border=“1” cellpadding=“10” width=“540” height=“120”>
  13. <tr ><th></th><th>阻断</th><th>严重</th><th>主要</th><th>次要</th><th>提示</th><th>总数</th></tr>
  14. <tr bgcolor=#ECFFFF><td>bugs</td><td align=“center”>{{bugs[0]}}</td><td align=“center”>{{bugs[1]}}</td><td align=“center”>{{bugs[2]}}</td><td align=“center”>{{bugs[3]}}</td><td align=“center”>{{bugs[4]}}</td><td align=“center” style=color:red>{{total_data[0]}}</td></tr>
  15. <tr bgcolor=#D2E9FF><td>漏洞</td><td align=“center”>{{leaks[0]}}</td><td align=“center”>{{leaks[1]}}</td><td align=“center”>{{leaks[2]}}</td><td align=“center”>{{leaks[3]}}</td><td align=“center”>{{leaks[4]}}</td><td align=“center” style=color:red>{{total_data[1]}}</td></tr>
  16. <tr bgcolor=#ECFFFF><td>坏味道</td><td align=“center”>{{tastes[0]}}</td><td align=“center”>{{tastes[1]}}</td><td align=“center”>{{tastes[2]}}</td><td align=“center”>{{tastes[3]}}</td><td align=“center”>{{tastes[4]}}</td><td align=“center” style=color:red>{{total_data[2]}}</td></tr>
  17. </table>
  18. <br></br>
  19. </body>
  20. </html>

9、添加执行自定义生成邮件模板步骤

前提:由于要执行sonar.py脚本,所以jenkins所在机器要有python3环境,且安装了pymysql、jinja2,

     centos安装python3:

进入到sonar.py所在目录,执行命令:python3 sonar.py 项目名

执行完成后会在/root/.jenkins/sonar_script目录下生成html文件,

10、配置发送邮件

10.1 安装插件:Email Extension

10.2 在系统管理》》系统设置中设置

11、发送邮件

在job中添加步骤

内容选择HTML,打开高级选项

增加触发器,并打开高级选项

输入发送邮箱列表,以英文逗号分隔,和邮件内容,html就是上面步骤生成的

最终的邮件如下图:

Please provide compiled classes of your project with sonar.java.binaries property

解决办法:

mvn命令添加sonar.java.binaries参数:

mvn sonar:sonar \
  -Dsonar.projectKey=fastjson \
  -Dsonar.host.url=http://127.0.0.1:9000 \
  -Dsonar.login=637e914c5ycyzharry6b690e765cffa7841f4aae \
  -Dsonar.java.binaries=target/classes

若target目录下没有生成classes目录,可以先build下项目,也可以直接写如下路径:

mvn sonar:sonar -Dsonar.java.binaries=target/sonar

CentOS7+STF环境搭建

一、简介

OpenSTF是一个手机设备管理平台,可以对手机进行远程管理、调试、远程手机桌面监控等操作,可以批量对大量设备进行WEB端管理。
STF (or Smartphone Test Farm) is a web application for debugging smartphones, smartwatches and other gadgets remotely, from the comfort of your browser.
二、环境搭建
1、基本命令安装
安装wget、zip等命令
yum install wget
yum install -y unzip zip
2、java环境配置
(1)下载jdk安装包并上传至Linux
因为在linux中直接使用curl或者wget下载jdk安装包会被重定向,所以我们要先在windows里先下载好所需的安装包再上传到linux中 jdk1.8官网下载地址 jdk1.9官网下载地址
我这里下载的包是:jdk-8u161-linux-x64.tar.gz

(2).解压安装
对安装包执行解压

[root@localhost ~]tar -zxvf jdk-8u161-linux-x64.tar.gz
解压出来一个文件夹jdk1.8.0_161
[root@localhost ~]# mv jdk1.8.0_161/ /usr/local/java
(3).配置环境变量
vi /etc/profile
在文件中增加如下内容:
export JAVA_HOME=/usr/local/java/jdk1.8.0_151
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
验证环境是否ok:
java -version

3.android SDK环境配置

首先下载SDK
cd /usr/local 
mkdir android 
cd android 
wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip 
unzip sdk-tools-linux-3859397.zip
配置环境变量
vi /etc/profile
在文件中增加如下内容
export PATH=$PATH:/usr/local/android/tools/bin
然后执行
source profile 
查看已安装信息
sdkmanager --list
安装platform-tools
sdkmanager "platform-tools"
配置环境变量
vi /etc/profile
在文件中增加如下内容:
export PATH=$PATH:/usr/local/android/platform-tools
执行
source profile sdkmanager --list
验证环境是否ok:
adb --version

4.node安装

cd /usr/local 
mkdir node 
cd node
wget https://nodejs.org/download/release/v9.0.0/node-v9.0.0-linux-x64.tar.gz 
tar xvf node-v9.0.0-linux-x64.tar.gz 
ln -s /usr/local/node/node-v9.0.0-linux-x64/bin/node /usr/local/bin/node 
ln -s /usr/local/node/node-v9.0.0-linux-x64/bin/npm /usr/local/bin/npm
检查环境
node -v 
npm -v

5.rethinkdb安装

执行:
cd /opt/dong -----即在opt新建一个目录用来用来其他依赖 
wget http://download.rethinkdb.com/centos/7/`uname -m`/rethinkdb.repo -O /etc/yum.repos.d/rethinkdb.repo
yum install rethinkdb
检查环境是否ok:
rethinkdb -v

6.GraphicsMagick安装

1.安装依赖
[root@localhost ~]# yum install -y gcc libpng libjpeg libpng-devel libjpeg-devel ghostscript libtiff libtiff-devel freetype freetype-devel
2、执行:
cd /opt/dong 
wget https://iweb.dl.sourceforge.net/project/graphicsmagick/graphicsmagick/1.3.26/GraphicsMagick-1.3.26.tar.gz 
tar xvf GraphicsMagick-1.3.26.tar.gz 
cd GraphicsMagick-1.3.26 
./configure 
make 
make install
验证环境是否ok:
gm

7.yasm安装

执行:
cd /opt/dong 
wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz 
tar xvf yasm-1.3.0.tar.gz 
cd yasm-1.3.0 
./configure 
make 
make install
验证环境是否ok:
yasm --version

8.libsodium安装

执行:
cd /opt/dong 
wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.15.tar.gz 
tar xvf libsodium-1.0.15.tar.gz 
./configure 
make 
make install
验证环境是否ok:
whereis libsodium

9.zeromq安装

执行:
cd /opt/dong 
wget https://github.com/zeromq/libzmq/releases/download/v4.2.2/zeromq-4.2.2.tar.gz tar xvf zeromq-4.2.2.tar.gz 
cd zeromq-4.2.2 
./configure 
遇到报错,yum install gcc-c++ 
make 
make install

10.protobuf安装

执行:
cd /opt/dong 
wget https://github.com/google/protobuf/releases/download/v3.5.0/protobuf-cpp-3.5.0.tar.gz 
tar xvf protobuf-cpp-3.5.0.tar.gz 
cd protobuf-3.5.0 
./configure --prefix=/usr/local/protobuf 
make 
make install
配置变量:
vi /etc/profile
在文件中增加如下内容:
export PATH=$PATH:/usr/local/protobuf/bin/ 
export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/
执行
source profile
验证环境是否ok:
protoc --version

11.pkg-config安装

执行:
cd /opt/dong 
wget http://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz 
tar xvf pkg-config-0.29.2.tar.gz 
cd pkg-config-0.29.2 
./configure --prefix=/usr/local/pkg-config --with-internal-glib 
make 
make install
验证环境是否ok:
pkg-config --version

12.stf安装

执行:
npm install -g stf
如果安装失败,先执行如下命令,再安装
npm config set unsafe-perm=true
下载完成后,进入:
cd /usr/local/node/node-v9.0.0-linux-x64/lib/node_modules/stf/bin 
./stf doctor

检查结果

[root@localhost bin]# ./stf doctor
INF/cli:doctor 10067 [*] OS Arch: x64
INF/cli:doctor 10067 [*] OS Platform: linux
INF/cli:doctor 10067 [*] OS Platform: 3.10.0-327.el7.x86_64
INF/cli:doctor 10067 [*] Using Node 8.9.1
INF/cli:doctor 10067 [*] Using ZeroMQ 4.2.2
INF/cli:doctor 10067 [*] Using RethinkDB 2.3.6
INF/cli:doctor 10067 [*] Using GraphicsMagick 1.3.26
INF/cli:doctor 10067 [*] Using ADB 1.0.39
INF/cli:doctor 10067 [*] Using ProtoBuf 3.5.0
ok,至此环境搭建成功

启动stf

进入stf目录:
cd /usr/local/node/node-v9.0.0-linux-x64/lib/node_modules/stf/bin 
./stf local --public-ip 111.67.203.89 --allow-remote (172.31.3.243位centos服务器地址即stf服务器地址)
在其他地址的电脑上,打开浏览器,输入
http://111.67.203.89:7100
即可访问stf

遇到的问题

1、zmq.node文件错误
转到stf工程根目录执行:
npm rebuild
备注:
如果上述命令失败,可能会出现一个死循环,按Ctrl+C停止,执行npm config set unsafe-perm=true,再执行上述命令

2、libzmq.so.5 not found

cd /etc
vi /etc/ld.so.conf
#新增一行:
/usr/local/zeromq/lib
ldconfig

3、远程访问失败
可能是防火墙未关闭,关闭方法

#检查防火墙状态
firewall-cmd --state
#关闭防火墙
systemctl stop firewalld.service
#禁止开机启动
systemctl disable firewalld.service

从0到1开发自动化测试框架

一、序言

随着项目版本的快速迭代、APP测试有以下几个特点:

  • 首先,功能点多且细,测试工作量大,容易遗漏;
  • 其次,代码模块常改动,回归测试很频繁,测试重复低效;
  • 最后,数据环境多样,用户场景复杂,功能回归覆盖难全面。

为节省成本,保证高效及高质量迭代,我们需采用更高效的测试方式,App自动化测试是较高效的手段。

之前自动测试实践过程中遇到的诸多问题(代码复用率低,Case开发及数据构造繁琐,问题定位困难,学习成本高等),为解决相关痛点问题,我们重新实现了一套APP自动测试框架。本文将着重介绍技术选型、设计思路及百度外卖App的具体实践。

二、自动化测试框架技术选型

一个项目中自动化测试是否能有效的展开,自动化测试框架是关键所在。因此,如何如何构建稳定的、易扩展的自动化的测试项目对于敏捷测试有重要的意义。在设计框架的时候应该尽可能的沿用自动化测试工具已提供的功能,避免重复开发,以减少开发成本。

通过对现有自动化测试工具的原理进行深入分析及优缺点比较,并基于Appium和TestNG两类自动化测试框架解决上述自动化测试中遇到的问题。

  • 首先,通过利用TestNG结合csv的使用,将测试用例数据转化为测试代码中的数据,减少了测试人员录入数据和准备数据的工具;
  • 再次,通过对appium的封装,按照面向对象的思想将测试中用到的页面元素封装成对象,增强测试代码的复用率,并减轻测试人员对底层代码实现的负担,提高测试代码编写效率;
  • 最后,引入失败重跑、失败截屏,并通过reportng生成测试报告的方式,逐步完善测试过程,提高定位问题的速度;
TestNG

Testng是一个开源自动化测试框架,引入了许多新的创新功能,如依赖测试,分组概念,使测试更强大,更容易做到。 旨在涵盖所有类别的测试:单元,功能,端到端,集成等。TestNG框架可以很好地帮我们完成WebDriver+java的页面自动化工作,通过各种注释的灵活运行,可以使你的测试用例更加完美,定制符合要求的测试用例

  1. TestNG是一个设计用来简化广泛的测试需求的测试框架,从单元测试到集成测试。
    这个是TestNG设计的出发点,不仅仅是单元测试,而且可以用于集成测试。设计目标的不同,对比junit的只适合用于单元测试,TestNG无疑走的更远。可以用于集成测试,这个特性是我选择TestNG的最重要的原因。
  2. 测试的过程的三个典型步骤,和junit(4.0)相比,多了一个将测试信息添加到testng.xml文件。
    测试信息尤其是测试数据不再写死在测试代码中,好处就是修改测试数据时不需要修改代码/编译了,从而有助于将测试人员引入单元测试/集成测试。
  3. 基本概念,相比junit的TestCase/TestSuite,TestNG有suite/test/test method三个级别,即将test/test method明确区分开了。
Appium

Appium一个开源、跨平台的测试框架,可以用来测试原生及混合的移动端应用。Appium支持iOS、Android及FirefoxOS平台测试。Appium使用WebDriver的json wire协议,来驱动Apple系统的UIAutomation库、Android系统的UIAutomator框架。
相比其他的移动自动化测试工具,Appium测试由于调用了Selenium的client库使其可以使用任意的语言,包括Python、Ruby、Node.js、Objective-C等。

三、自动化测试框架的设计思路

测试设计过程和测试自动化框架必须作为两个单独的实体来开发。
测试框架应该独立于应用程序;
测试框架应该易于扩展 、维护和增强;
测试策略/设计应该对测试者隐藏测试框架的复杂性。

四、自动化框架介绍

该框架基于Selenium WebDriver开源技术开发。本框架使用Maven工具进行Project管理,采用TestNG工具组织测试,应用CSV文件存储测试数据,实现测试数据与测试用例的分离,方便测试数据管理,降低自动化脚本的维护成本,实现数据驱动。此外,该框架还封装了丰富的Selenium方法关键字,借鉴了QTP语法结构,实现了直观清晰的结构化代码语法,如:Page.Item.Operate,降低自动化代码的冗余与重复。借助Jenkins 进行CI测试,实现测试任务的Schedule 和Report功能,通过Jenkins Master/Slave模式管理虚拟机节点,实现多任务多机器分布式并发的执行管理,从而提高测试效率。

  • 该框架的好处在于:
    1、构建可复用的、稳定的代码集。通过封装appium实现用例执行与数据调用分离,参数化配置常用信息,并提供统一接口;

2、模块化管理自动化测试用例。主要根据TestNG工具的支持参数测试和依赖测试的特点实现;
3、测试结果分析和统计。利用jenkins工具建立持续集成,定期运行自动化测试项目,并将测试结果以定制化的形式展现。

测试框架分层

基于UI测试,我们希望除了支持web测试,还能支持app的测试,可能还需要接口测试,我们就需要考虑分层问题,将测试框架分为三层。上层是管理整个自动化测试的开发,执行以及维护,在比较庞大的项目中,它体现重要的作用,它可以管理整个自动测试,包括自动化测试用例执行的次序、测试脚本的维护、以及集中管理测试用例、测试报告和测试任务等。下层主要是测试脚本的开发,充分的使用相关的测试工具,构建测试驱动,并完成测试业务逻辑。

第一层:数据层

即执行用例时所需要的测试数据,如商户名、空间名、URL等,这些数据用来支撑整个脚本的执行。针对数据层,这里采了用数据驱动的方式。

第二层:驱动层

这一层主要封装各种driver。比如我们针对网页测试,使用selenium-webdriver开发包,针对app测试,我们使用appium开发包。我们在这一层进行封装,通过调用selenium-webdriver,appium提供的原生方法,封装成可读性很强的方法且加上容错机制。以后就算我们要换用其他的第三方包,我们的测试案例层和支持层的方法也不需要做任何的修改。只需要修改driver层实现的方式就可以了。在一层,我们主要实现两个方面的封装,一个是driver的封装,一个是基于基类自然语言函数的封装。

driver封装

我们需要封装,根据参数确实是基于web测试还是基于app测试。比如:

基类封装

主要是封装各种可读性很很强的方法以及将元素定位标识及driver也封装进去。为了支持网页测试和app测试,我们需要两个基类,一个是针对网页操作基类,一个是针对app操作基类。同时为了web和app操作的一致性,我们要求对外提供的方法,必须要将常用的方法保持一致的名字和一样的参数类型及参数个数。
APP基类示例如下:

通过对driver和基类的封装,driver层实现了对网页测试和app测试的支持,并且针对两种测试,都提供了统一的方法,能够方便使用者,使用相同的方法,测试app和web。

第三层:测试案例层
该层是测试案例的具体实现,就像上面写的case那样,用接近自然语言的方式,来实现测试案例。

第四层:支持层
该层主要提供workflow,通用工具,元素库的支持,便于测试案例层直接调用。

  • Workflow:主要封装测试项目中需要经常使用的针对项目的公用方法,供测试案例层直接调用。比如用户登录,注册一个用户,搜索出用户等等经常使用的动作;
  • 通用工具:提供一些通用方法,比如生成指定Page类,文件读取操作,DB操作,http操作支持等等;
  • 元素库:每一个页面元素的定位表达式(xpath,id,name,css,link_text等等表达式)。
    我们的测试案例,都是针对一个个元素进行操作的。将每一个页面的每一个元素,都看成一个继承了基类的特定类。所以,我们的第一步,就需要找到这个元素,定位到这个元素。测试项目的所有元素都放到这里。

第五层:结果保存层
将测试脚本的日志和结果以自定义的方式展示,这里使用了ReportNG,它可以丰富测试结果的展现形式,帮助团队更快定位和解决问题。

五、框架技术要点解析

5.1、PO模式

5.1.1、遇到的问题

使用webdriver做过一段时间的测试就会发现一个对某一个页面的元素进行定位的时候,程序行间充斥着id()、name()、xpath()等方法,这样会造成测试程序的可读性较差,不便于后期的维护以及修改。
虽然我们可以通过添加注释的方法使程序便于理解,但是还是不可以从根本上解决这种问题。我们可以通过对这些方法进行二次封装来避免每次对这些方法的直接调用,通过方法的封装虽然可以实现复用。但是我们发现通过封装无法实现页面元素的逻辑处理和测试数据的独立。

5.1.2、问题的解决办法:引入PO

Page Object模式是Selenium中的一种测试设计模式,是指UI界面上用于与用户进行交互的对象。主要是将每一个页面设计为一个Class,其中包含页面中需要测试的元素(按钮,输入框,标题 等),这样在Selenium测试页面中可以通过调用页面类来获取页面元素,这样巧妙的避免了当页面元素id或者位置变化时,需要改测试页面代码的情况。 当页面元素id变化时,只需要更改测试页Class中页面的属性即可。通过对界面元素的封装减少冗余代码,提高测试用例的可维护性。

一般情况下,对于一个Page Objects对象,它有两个方面的特征:
• 自身元素(WebElement)
• 实现功能 (Services)

仔细分析测试场景,抽出UI测试的核心行为,无非就是:
1、检查点:
页面元素是否存在;
页面元素显示内容是否正确;
页面元素是否可用;
……
2、辅助功能:
等待元素出现;
点击某页面元素;
给元素输入内容;
……

分析抽出来的核心行为,发现这些行为基本都是针对一个个页面元素进行的操作。那么我们就可以做如下的动作:
将页面元素看成一个对象,封装成一个类;
将上面分析得到的核心行为都封装成基类方法。然后确保,任何一个页面元素都继承该基类;
该基类提供类似于自然语言的方法名字,调用这些方法,就能很明确的知道测试案例在做什么检查,在做什么行为,这样就能极大的提高测试案例的可读性。
该基类主要目的是在UI测试中,对元素共性的检查点和辅助方法进行抽取,将它们封装成一个个非常容易读懂的方法,且具有异常处理能力。
经过上述思路的整理,测试用例可以改写成如下:

在实际的使用过程中,可以让不太熟悉代码的QA专门负责测试案例的实现,底层的方法包装可以由经验丰富一些的同学做。

5.2、数据驱动

数据驱动的自动化测试框架是这样的一个框架,从某个数据文件(例如ODBC源文件、Excel文件、Csv文件、ADO对象文件等)中读取输入、输出的测试数据,然后通过变量传入事先录制好的或手工编写的测试脚本中。其中,这些变量被用作传递(输入/输出)用来验证应用程序的测试数据。在这个过程中,数据文件的读取、测试状态和所有测试信息都被编写进测试脚本里;测试数据只包含在数据文件中,而不是脚本里,测试脚本只是一个“驱动”,或者说是一个传送数据的机制。
1、在数据文件中填写测试数据:

2、生成Page类:

3、Page类中初始化页面元素:

基于数据驱动的好处在于:
在应用程序开发的同时就可以同步建立测试脚本,而且当应用功能变动时,只需要修改业务功能部分的脚本;
利用模型化的设计,避免重复的脚本,减少建立或维护脚本的成本。

5.3、失败重跑与失败截图机制

自动化测试过程中,常常由于网络、服务器响应过慢、JS特效及页面渲染时间较长,导致自动化测试失败。针对此类场景,本框架设计了一套NRetry机制,即某个case运行失败后,重跑N次,N可自定义。N次中有一次成功,则继续运行,若N次均失败,则截图、抛错,停止运行。NRetry机制,一定程度上可以降低由于网络、服务器响应过慢导致的自动化执行的不稳定性。

5.3.1、失败自动截图

1、新建一个Java类继承TestListenerAdapter:

2、重写onTestFailure、onTestSkipped等方法,在这些方法中加入截图操作:

3、在testng.xml文件中配置自己编写的监听器类:

5.3.2、失败自动重跑

在运行自动化测试用例的时候,经常会出现一些异常的情况的情况导致用例失败的问题。所以我们可能会希望对于失败的测试用例再重新运行一次,框架中我们结合TestNG来实现这个功能。

  1. 新建TestNGRetry类,实现用例失败自动重跑逻辑:
  2. 添加用例重跑监听器RetryListener,用例失败自动重跑功能:
  3. 在testng.xml文件中配置自己编写的监听器:

5.4、ReportNG

TestNG默认的HTML报表,其默认的报表虽然信息全面,但不易于理解。因此,我们利用ReportNG来替代TestNG默认的report。
ReportNG提供了一种简单的方式来查看测试结果,并能够对结果代码进行着色。还可以通过修改CSS文件来替换默认的输出样式。此外ReportNG还能够生成Junit格式的XML输出。
由于我们使用的是maven,所以我们主要来看看pom.xml的情况:

maven-surefire-plugin 这个插件主要是用于testng的。我们通过该插件,在对应的目录下./target/${timestamp}生成我们的测试报告目录。我们可以看到这个目录的结构。

这里实际上就是reportng的测试报告的生成路径。但是我们想要通过邮件发送会很难,因为html的内容需要加在额外的css,以及js文件。而邮件实际上是不支持外部的css以及js文件的。
HTML的生成

1、定义HTML模版

查看indexMain.html.vm:

下面我们看下生成报告的部分核心代码:

在使用ReportNG替换TestNG自带报告时如果报告中含中文,由于ReportNG 里面Log 是不支持中文的,所以通过修改ReportNG.jar源码来支持报告内中文展示。

生成定制化后的最终报告,如下图所示:

5.5、日志收集

日志是软件开发的重要组成部分。自动化执行过程的日志信息,对于失败用例的分析定位以及全过程的跟踪记录是十分重要的。
相对于简单的输出打印,本框架集成了主流的日志收集工具log4j。Log4j是高度可配置的,并可通过在运行时的外部文件配置。通过配置log4j.properties文件,定义日志级别内容及日志输出路径收集日志信息(诸如:数据库,文件,控制台,UNIX系统日志等),提供快速的调试,维护方便,以及应用程序的运行时信息结构化存储。
配置文件
Log4j可以通过java程序动态设置,该方式明显缺点是:如果需要修改日志输出级别等信息,则必须修改java文件,然后重新编译,很是麻烦;log4j也可以通过配置文件的方式进行设置,目前支持两种格式的配置文件:
xml文件;
properties文件(推荐)。
下面是一个log4j配置文件的完整内容:

5.6、邮件发送

测试报告的发送可以结合Jenkins来实现,通过简单的配置即可实现。可是如果团队没有搭建jenkins或者有时jenkins不可用,我们应该如何去处理这部分的内容呢?
既然邮件不能够依赖jenkins,那肯定得自己去实现这部分的内容了。所以我们还是得依赖一些第三方的jar包。我们在pom.xml配置。

具体的发送邮件的部分代码如下:

六、后续TODO

在后续的版本演进中,将把自动化测试、代码安全扫描、多机并行测试、持续发布都加入了整体过程,真正的做到全过程持续交付。

6.1、夜间构建加入自动化测试

夜间构建会按计划定期触发自动化构建过程,但这种构建只是简单的代码编译,没有可靠的或可重复的功能测试。后续考虑Appium结合Jenkins来实现构建后自动化测试工作。
无论任何时候,只要代码更新提交到git中,构建服务器就会触发一个构建,构建运行脚本去编译应用程序并且运行一系列的自动化单元测试和/或集成测试。通过自动化测试结果能够清晰的展示出那些功能特性是通过的,哪些是失败的。不管是有改动提交,还是定期在夜间触发构建,应用程序都会被自动部署到测试环境当中以便QA团队进行测试。

6.2、Jenkins与STF结合,实现多机并行测试

Jenkins构建脚本完成后,将没有安装stf组件电脑上连接的android设备,添加映射到装有stf平台服务的机器上,将集成测试用例push到STF平台,再由STF分发到可运行设备上,进行多机并行测试。
STF执行APPIUM测试带来的优势

第一、可以在真机上执行并行的Appium测试。由于最初的Appium使用对象是模拟器上或只是以每次一台设备的测试方法执行测试,而STF在原有的基础上扩展了Appium,最多可在数百台真机上同时执行测试的能力。
第二,不需要配置任何设备的Desired Capabilities。这种方式既简便,且减少了因为编辑脚本而产生的不同类型的错误。
第三,在STF上执行测试可以让用户即时浏览测试状况。也就是说,可以查看到测试执行的进度,即时的错误反馈,以及保留和查阅所有测试项目,测试脚本和测试结果(测试截图,测试日志,性能数据等)

6.3、代码质量度量

6.3.1、为什么要分析代码

对代码质量关注时,安排人工进行code review是需要的,但100%的code review却需要投入人员,消耗大量的工作量,而工具自动检查只需少量人工配置。
最主要的原因就是提高代码质量,了解RD在编码过程中犯过的错误可能对功能逻辑产生的影响,同时也推动RD让自己的代码更具有可读性和维护性,所以我们借鉴持续改进的流程,希望能够在这个过程中有所收获。

6.3.2、Jenkins引入Sonarqube进行代码持续审查

Sonar是一个用于代码质量管理的开源平台,用于管理Java源代码的质量。通过插件机制,Sonar 可以集成不同的测试工具,代码分析工具,以及持续集成工具,比如pmd-cpd、checkstyle、findbugs、Jenkins。通过不同的插件对这些结果进行再加工处理,通过量化的方式度量代码质量的变化,从而可以方便地对不同规模和种类的工程进行代码质量管理。

6.4、email-ext实现Jenkins邮件通知功能

在Jenkins中配置实现邮件通知,Jenkins提供了两种方式的配置。
一种是Jenkins内置默认的邮件通知,但是它本身有很多局限性,比如它的邮件通知无法提供详细的邮件内容、无法定义发送邮件的格式、无法定义灵活的邮件接收配置等等。
在这样的情况下,后续考虑可以通过Email Extension Plugin来实现自定义邮件通知的方方面面,比如在发送邮件的同时可以自定义发送给谁,发送具体什么内容等等。

python+minicap的使用

说起Minicap,不得不提到STF,STF (Smartphone Test Farm) 是一个开源的web架构应用,用户可通过浏览器远程操作Android设备、调试Android应用、在设备上进行测试,实现真正意义云端使用、调试、测试、管理真机器。STF出现以后,国内几个大互联网公司也纷纷跟进效仿,出现了类似的真机调试、管理平台,较为知名的有腾讯Wetest、阿里MQC、百度MTCTestIn等。可见远程真机调试在移动研发领域的作用还是受到了比较高的重视,也能为公司以及用户带来比较直接的收益。下面是STF官方的介绍动画。

STF

minicap简介

minicap属于STF框架的一个工具,由STF团队自身开发,属于较为核心的一部分,minicap运行于android设备端,负责设备屏幕视频的实时采集并通过socket接口发送,github下载地址:https://github.com/openstf/minicapmicicap。minicap采集屏幕的原理很简单:通过ndk的截屏接口不停的截屏并通过socket接口实时发送,这样客户端便可以得到一序列的图片流,图片流合成后就成为视频;

构建minicap

micicap由Android ndk开发,包含一个可执行的二进制文件以及一个so文件,运行minicap前,需要通过adb命令将设备对应CPU架构以及设备对应SDK版本的minicap文件拷贝到设备后,再执行。由于github上并没有上传编译完成后的产物,因此我们需要自行编译。

编译依赖环境:

1)、NDK;

2)、make;

3)、git;

环境依赖较为简单,如果没有NDK以及make环境的,可自行百度安装;

构建过程:

1)、通过git下载minicamp源码:

git clone https://github.com/openstf/minicap.git

2)、micicap项目还依赖于libjpeg-turbo,首先我们需要在minicap引入libjpeg-turbo项目源码:

git submodule init

git submodule update

3)、执行ndk-build,构建完成后,minicap编译后的产物将会在libs目录下找到;

ndk-build

运行minicap

1)、获取设备CPU支持的ABI,minicap针对4种不同的ABI构建了不同的so文件和可执行文件,分别是:x86_64/x86/arm64-v8a/armeabi-v7a;

ABI=$(adb shell getprop ro.product.cpu.abi|tr -d’\r’)

2)、拷贝对应ABI版本的文件到设备,这里使用的是adb push;

adb push libs/$ABI/minicap /data/local/tmp/

3)、获取设备对应的SDK版本;

SDK=$(adb shell getprop ro.build.version.sdk|tr -d’\r’)

4)、只有可执行文件是不够的,我们还需要拷贝对应sdk版本的共享库到设备;

adb push jni/minicap-shared/aosp/libs/android-$SDK/$ABI/minicap.so /data/local/tmp/

5)、每次启动minicap,我们都需要设置LD_LIBRARY_PATH,不然会提示找不到公共库,-P后面的参数为:{RealWidth}x{RealHeight}@{VirtualWidth}x{VirtualHeight}/{Orientation},可以指定采集的实际大小、虚拟大小以及屏幕方向,实际大小一般设置成设备物理分辨率大小,虚拟大小是通过socket接口发送的大小,屏幕实际窗口大小我们可以通过adb命令获取;

adb shell dumpsys window | grep -Eo ‘init=\d+x\d+’ | head -1 | cut -d= -f 2

6)、启动minicap,下面我们假设获取到的实际屏幕大小是1080×1920,需要发送的虚拟窗口大小是540×960,采集的屏幕方向是纵向;

adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 1080×1920@1080×1920/0

7)、端口转发,通过adb forward命令,可以把minicap端口映射到我们PC指定的端口,localabstract:minicap是UNIX域名的SOCKET名称,把minicap的socket端口映射到PC的1313端口,这样我们就可以在PC通过连接1313端口获取到设备的实时视频流;

adb forward tcp:1313 localabstract:minicap

minicap协议解析

minicap启动并用adb forward命令映射端口后,我们就可以通过socket与minicap建立连接。

1)、Global header

minicap协议是一种简单的二进制流推送流协议,一旦与minicap建立连接,minicap首先会推送长度为24字节的global header,global header只会推送一次,后续推送的数据不会再包括global header,而是不断的推送实时图片流数据,直到客户端关闭socket连接。

Global header binary format

Global header说包含了基本的一些信息,如minicap的版本信息、头长度、实际大小以及虚拟大小、设备方向等,这些信息我们可以保存起来,方便后面使用,这里我使用python解析了Global header,代码参考如下:

2)、Frame binary format

接下来,minicap会不断的推送一帧一帧的图片流,每一帧都包含两部分信息:0-3字节,表示这一帧图片的长度n,由4个字节的32位整型小端格式存储;4-(n+4)字节,是具体的图片数据,由JPG格式存储,这部分才是我们想要的最关键数据;

Frame binary format

至此,我们完成了minicap协议的解析,并获取到了minicap推送过来的每一帧图片。需要注意的是,由于minicap是实时推送流,因此流的数据可能会比较大,客户端获取的buffer需要尽可能的大,不然我们在渲染每一帧的时候,可能会出现卡顿的现象,具体多大合适,我们可以稍微推算一下,一张由minicap推送过来的1080×1920大小的png图片,大概是100-200KB,minicap宣称帧率可以达到20 FPS左右,因此我们的buffer可以设置为:200KB * 20 = 4096000字节,每隔一秒recv()一次;

PyQt实时渲染

获取到图片流数据后,我们可以使用PyQt中的paintEvent进行渲染,下面的refreshFrame()方法,关联了获取图片线程中的一个信号槽,一旦获取图片线程从minicap解析到一帧的图片,便会通知refreshFrame()中的self.update()方法,self.update()方法则会调用paintEvent进行界面的刷新:

美团点评云真机平台实践

背景

随着美团点评业务越来越多,研发团队越来越庞大,对测试手机的需求显著增长。这对公司来说是一笔不小的开支,但现有测试手机资源分配不均,利用率也非常有限,导致各个团队开发、测试过程中都很难做到多机型覆盖。怎么样合理、高效利用这些测试手机资源,是摆在我们面前的一道难题。

现有的方案

为了解决这些问题,业内也出现了一些手机管理和在线调试使用的工具或平台,比较常见的有:

  • OpenSTF
  • 百度MTC的远程真机调试
  • Testin的云真机
  • 腾讯WeTest的云真机
  • 阿里MQC的远程真机租用

其中OpenSTF是开源项目,其他的平台大多也都是基于OpenSTF原理实现的。因此,我们对OpenSTF项目进行了深入研究。

遇到的问题

我们首先按照OpenSTF官方的方案进行了搭建,并进行了小规模的应用,但渐渐的我们发现了它的一些问题:

  • 模块过多而且耦合紧密,解耦难度较大,每次修改需要更新所有模块,难以快速迭代开发。
  • 部分技术选型落后。由于OpenSTF出现的时间比较早,部分技术已经落后于目前的主流。例如OpenSTF前端选用AngularJS 1.0进行开发,在生态链方面已经落后于其他流行的框架;数据库方面选用非关系型数据库RethinkDB,在数据计算和性能方面弱于MySQL等关系型数据库,同时RethinkDB资料较少,不便于开发与维护。
  • OpenSTF屏幕图像传输采用图片单张传输的方式进行,而且画质不能由用户来调节,实际应用中占用带宽很高,在网络比较差的情况下会有严重的卡顿现象,体验很差。

我们的方案

架构设计

根据业务场景的需要,并吸取了OpenSTF结构优点,我们采用Agent/Server模型的模块化设计方案。下面分别介绍主要模块的功能:

  • Agent模块。Agent模块与OpenSTF的provider类似,部署在服务器上或者用户的电脑上,Agent连接真实的手机,并且将手机的屏幕图像通过Websocket动态代理到Websocket服务器上,然后通过消息中心来进行消息的传递。
  • Server模块。Server用来集中管理和调度手机,与OpenSTF结构不同的是,我们的Server端包含Web服务器、Websocket服务器、动态代理以及消息处理服务等部分,Server将用户的访问动态代理到对应的Web服务器和Websocket服务器上,并通过消息处理模块向消息中心传递消息,实现用户与Agent端手机的交互。
  • 数据存储模块。数据存储模块用来保存整个平台的数据,例如手机的状态、用户使用记录等。数据存储模块由MySQL数据库和一个RPC服务组成,Server不再直接读写数据库,而是通过一个RPC服务来进行数据的读写操作。
  • 消息中心。与OpenSTF的triproxy功能类似,是连接手机和Server的枢纽,消息中心主要处理屏幕的操作以及手机的状态变更等消息。

通过模块化设计,项目结构比OpenSTF更加清晰。下面是整个系统的设计图:

架构的优势

Agent模块我们直接复用了OpenSTF的provider大部分功能,包括minicap、minitouch等。在此基础上,我们也扩展了一部分OpenSTF缺失的功能,比如:

  • 在provider的基础上进行了二次开发,使其支持画质/帧率调节(下文会有详细说明)。
  • 加入健康检测功能,检测手机网络是否正常、是否设置了网络代理等。
  • 加入Inspector功能,方便获取UI控件树(下文会有详细说明)。
  • 对Agent进行了版本区分,便于Web端根据不同的Agent版本对相应的功能展示和隐藏。

在Server模块中,我们引入了动态代理的机制,并且重新开发了Web部分,采用了Vue 2.0 + iView来实现,数据库采用了MySQL。

相比OpenSTF原生架构,总结下来有以下优势:

  • 模块耦合程度低,开发和部署更方便。OpenSTF各个功能模块不仅数量多而且代码耦合紧密,在此基础上进行二次开发和部署非常困难。而我们将整个项目分为Server、Agent、消息中心、数据存储四个模块,四个模块功能和代码都是独立的,基本上没有耦合关系,支持快速迭代开发,部署也很方便。
  • 前端框架更主流,开发和维护成本低。OpenSTF前端是使用AngularJS 1.0实现的,AngularJS 1.0已经处于废弃阶段,各种第三方组件基本已经停止支持,AngularJS 2.0的社区和生态并未成熟,而我们采用了Vue 2.0前端框架,Vue 2.0相对已经成熟,在美团侧也已经有大量应用,能够快速开发高质量的Web功能。
  • 数据库性能强,设计灵活、维护方便。OpenSTF使用RethikDB作为数据库,RethikDB是一个NoSQL型数据库,它有非常多的缺点,比如处理大量数据时的性能很差,资料非常匮乏,排查问题和数据库维护都非常困难。而我们采用了MySQL数据库,很好的解决了这些问题,并且实现了Server模块与数据读写的分离,这样在更新数据库表结构的时候无须同时修改Server端的代码,只要保证RPC服务的接口格式一致即可,开发和维护更加方便。

除了这些基础的功能之外,我们还开发了一些特色的功能,下面我们来详细介绍。

特色功能

与客户端自动化相结合

为了合理、高效利用测试手机资源,我们与客户端自动化进行了结合,主要有两个方面:

  • 与集团内部的云测平台深度融合。在云测平台的服务器节点上部署Agent代码,为云测平台自动化任务创建者提供自动化过程展示和远程调试功能,同时将云测平台空闲的手机开放给更多人使用。
  • 开放API。我们开放了一些API给普通用户,供用户查询手机状态、占用手机、连接adb调试等,用户可以使用脚本调用API,然后直接在平台的手机上进行自动化测试。

预约功能

当一台手机处于繁忙状态时,用户必须要等待手机空闲后才能使用,由于手机空闲时间不确定,就会给用户带来很大的不便。为了解决这个问题,我们开发了手机预约的功能,用户可以预约处于繁忙状态的手机,当手机空闲时,自动帮预约用户占用15分钟,并通过即时通讯工具通知预约人。

画质调节

远程调试平台的核心是实时获取屏幕图像,由于屏幕传输需要比较大的网络带宽,在网络不佳的情况下就会出现卡顿现象。因此,我们针对不同的网络做了一些流畅度的优化,下面来介绍一下其中的细节。

屏幕获取的原理是通过minicap来高速截图,每秒最高可达60张,然后将这些截图显示在Web上。因此,我们考虑从两个方面来优化网络带宽的占用,第一个是调节截图的质量,minicap本身支持调节画质(OpenSTF固定设置了80%的压缩比),关键代码如下:

var rate =  Number(match[6])
    if (rate > 2 && rate < 100) {
      log.info(rate)
      if (rate > 30) {
        options.screenJpegQuality = 80
      }else if (rate > 15) {
        options.screenJpegQuality = 50
      }else {
        options.screenJpegQuality = 20
      }

      frameProducer.restart()
      framerate = rate
    }

第二个是调节每秒发送的图片张数,也就是帧率,我们可以在Agent端控制发送图片的数量,关键代码如下:

# 首先修改帧率发送部分:
function wsFrameNotifier(frame) {  
  if (latesenttime == 0 || Date.now()-latesenttime > 1000/framerate) {  
    latesenttime = Date.now()  
    return send(frame, {  
      binary: true  
    })  
  }  
}
# 再加入调整帧率的代码:
case 'rate':  
  var rate =  Number(match[6])  
  if (rate > 2 && rate < 100) {  
    framerate = rate  
  }  
  break

那么,帧率和图片压缩比调节到多少才能满足不同网络环境的需要呢?我们先来看一组数据:

图片压缩比 图片尺寸
100% 69.82KB
80% 46.78KB
50% 41.87KB
20% 37.84KB
10% 35.84KB

表中是使用minicap做的图片压缩实验,从数据中我们可以看到当图片质量降低到80%时图片大小降低比较明显,而图片质量并没有明显的下降。继续降低图片质量,图片大小降低有限。我们再来看另外一组数据:

帧率 图片压缩比 实际流量
60 100% 4.02M/S
60 80% 2.74M/S
60 50% 2.41M/S
30 80% 1.43M/S
30 50% 1.22M/S
30 20% 1.10M/S
15 50% 0.63M/S
15 20% 0.55M/S
15 10% 0.52M/S

表中是各种帧率和压缩比组合产生的实际流量数据。

从数据中我们可以看到最高帧率和压缩比的组合下,流量达到了4M/S,而80%压缩比时流量减小到了2.7M/S,降低非常明显。考虑到实际网络情况,我们将60帧、80%压缩作为了高画质选项

而图片质量从80%降低到50%时图片大小下降并不明显,此时降低帧率就成了很好的选择。当帧率降低到30帧时流量降低了一半,1.2M/S的流量能够满足大部分网络状况使用,30帧也能保证操作的流畅度,于是30帧、50%压缩比成为了中画质的选项

低画质主要是为了保证在较差的网络环境能够正常使用,500K/S的流量是红线。我们将15帧、20%压缩比作为低画质选项,此时图片质量和帧率较低,但能够保证基本的使用体验。

除了通过降低图片质量和帧率来减小手机屏幕图像传输的流量外,将图像使用H264等编码压缩成视频传输也是一种有效降低流量的办法,相对于图片,图像的压缩率将会更高,用户的操作体验也会更好。但是图像压缩编码原理比较复杂,相关技术我们还在研究当中。

App Mock

在做App测试过程中经常需要抓包,一般情况下,我们通过修改WiFi的代理然后用抓包工具就可以实现。但是这样做的效率比较低,多个工具切换也非常不便。借助集团的Mock平台,我们开发了一键Mock功能,能够快速完成相应App的Mock操作。带来的好处是我们可以一边操作App,一边查看App发出的请求,大大提高了测试的效率。

App Inspector

App Inspector功能可以让用户在平台上使用真机的同时查看页面控件树及页面元素,并且支持Xpath,更加方便高效的查找页面元素,给UI自动化测试提供了很大便利。

这个功能我们是借助Uiautomator实现的。基本原理是写一个Uiautomator用例,用来获取当前页面的Hierarchy,然后将用例打包成一个JAR放到Agent端。当在Web端触发获取控件树时,Agent将JAR包推送到手机上并运行,此时会在手机端生成一个XML文件。然后再使用cat命令获取XML内容并在前端解析。用例核心代码如下:

public class launch extends UiAutomatorTestCase {

    public void testDumpHierarchy() throws UiObjectNotFoundException {
        File file = new File("/data/local/tmp/local/tmp/uidump.xml");
        UiDevice uiDevice = getUiDevice();
        String filename = "uidump.xml";
        uiDevice.dumpWindowHierarchy(filename);
    } }

当然,你也可以用adb命令来获取Hierarchy:

adb shell uiautomator dump /data/local/tmp/uidump.xml

但这种方式不能获取动态页面,比如视频播放页面。

数据报表

为了更好的了解平台的运营情况,我们做了详细的数据统计,主要从使用次数、使用时间、设备数量、使用分布等方面进行统计。目前我们管理的手机近300台,平均每个月有超过500名研发人员在使用我们的平台,每天的使用次数达到280次,每天总使用时长超过60小时。

其他小功能

除了以上几个比较大的功能点,我们也做了一些贴心的小功能,比如:检测手机网络是否设置代理、检测手机已安装的应用版本及安装时间、快速安装最新版本的测试包、支持App内的Schema跳转等等。这些小功能为研发人员节省了很多时间,提升了他们的工作效率。

未来规划

iOS手机支持

目前,云真机平台只支持Android手机,对iOS手机的支持正在进行中。我们已经初步完成了主要功能的开发,预计很快将与大家见面。

产品优化

我们计划继续扩展产品功能,比如支持Log日志展示和性能数据采集等。目前云真机平台已经在美团点评内部平稳运行超过两年,我们会继续不断迭代版本、打磨产品,提供更好的使用体验。

 

CentOS7 安装远程桌面

Xrdp是Microsoft远程桌面协议(RDP)的一个开源实现,它允许以图形方式控制远程系统。使用RDP,您可以登录到远程计算机并创建一个真正的桌面会话,就像您登录到本地计算机一样。
系统环境
  • 服务端:Centos7.7 Minimal
  • 客户端:Windows10
安装桌面环境

本实验中安装的系统没有安装桌面环境,我们需要自己安装,如果已经安装桌面了清跳过这一步。Centos7提供了”Cinnamon Desktop”,”MATE Desktop”,”GNOME Desktop”,”KDE Plasma Workspaces”,”LXQt Desktop”,”Xfce”让我们安装。

下面的命令列出可用环境组:

[root@localhost ~]# yum grouplist
Loaded plugins: fastestmirror
There is no installed groups file.
Maybe run: yum groups mark convert (see man yum)
Loading mirror speeds from cached hostfile
 * base: mirrors.tuna.tsinghua.edu.cn
 * epel: mirrors.aliyun.com
 * extras: mirrors.aliyun.com
 * updates: mirrors.aliyun.com
Available Environment Groups:
   Minimal Install
   Compute Node
   Infrastructure Server
   File and Print Server
   Cinnamon Desktop
   MATE Desktop
   Basic Web Server
   Virtualization Host
   Server with GUI
   GNOME Desktop
   KDE Plasma Workspaces
   Development and Creative Workstation
Available Groups:
   Cinnamon
   Compatibility Libraries
   Console Internet Tools
   Development Tools
   Educational Software
   Electronic Lab
   Fedora Packager
   General Purpose Desktop
   Graphical Administration Tools
   Haskell
   LXQt Desktop
   Legacy UNIX Compatibility
   MATE
   Milkymist
   Scientific Support
   Security Tools
   Smart Card Support
   System Administration Tools
   System Management
   TurboGears application framework
   Xfce
Done

我们可以选择自己喜欢的桌面环境,在这里选择安装Xfce桌面:

[root@localhost ~]# yum -y install epel-release && yum groupinstall Xfce
安装Xrdp
[root@localhost ~]# yum -y install xrdp

安装完成之后,设置开机启动并启动xrdp

[root@localhost ~]# systemctl start xrdp && systemctl enable xrdp

创建~/.Xclients,设置默认启动xfce4桌面

[root@localhost ~]# echo "xfce4-session" > ~/.Xclients
[root@localhost ~]# chmod +x .Xclients
在客户端远程连接

Centos7安装Xrdp远程桌面服务Centos7安装Xrdp远程桌面服务

Centos7安装Xrdp远程桌面服务Centos7安装Xrdp远程桌面服务

Centos7安装Xrdp远程桌面服务Centos7安装Xrdp远程桌面服务

CentOS7.5安装Redis问题:fatal error: jemalloc/jemalloc.h: No such file or directory

cd src && make all

make[1]: Entering directory `/root/redis-4.0.14/src’

CC Makefile.dep

make[1]: Leaving directory `/root/redis-4.0.14/src’

make[1]: Entering directory `/root/redis-4.0.14/src’

CC adlist.o

In file included from adlist.c:34:0:

zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such file or directory

#include <jemalloc/jemalloc.h>

^

compilation terminated.

make[1]: *** [adlist.o] Error 1

make[1]: Leaving directory `/root/redis-4.0.14/src’

make: *** [all] Error 2

上面抛出 jemalloc 的原因是 jemalloc 重载了 Linux 下的 ANSI C 的 malloc 和 free 函数。解决办法:make时添加参数,如下:

make MALLOC=libc

struct redisServer’ has no member named ‘maxmemory’

  1. #升级到 5.3及以上版本
  2. yum -y install centos-release-scl
  3. yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
  4. scl enable devtoolset-9 bash
  5. #注意:scl命令启用只是临时的,推出xshell或者重启就会恢复到原来的gcc版本。
  6. #如果要长期生效的话,执行如下:
  7. echo “source /opt/rh/devtoolset-9/enable” >>/etc/profile

jenkins构建结果企业微信提醒

每当jenkin在构建之后我们想把构建结果SUCCESS/FAILURE或者其他信息通知给其他人,也许有人会说,不是有邮件提醒吗?但是我这里的环境邮件提醒的话所被通知者并不会第一时间去阅读,所以我们用“钉钉”,“企业微信”来第一时间去通知你想通知的小伙伴们。

因为我这里的环境用的是企业微信所以本文都是基于企业微信来说的,不过在钉钉上也可以使用,也可以利用钉钉在jenkins上的插件,那样更方便些。

进入正题

我们要获得jenkin的构建结果是SUCCESS还是FAILURE是需要jenkin构建完毕才能获取到的,那么我们从哪里能获得构建结果以及其他信息呢?

我们可以用日志信息或者api的构建信息来获取

我们 我们可以用日志信息或者api的构建信息来获取,这里我们是基于查看日志来查看构建结果以及其他信息,我这里以windows环境以及jenkins默认安装环境来举例

每一次日志文件的位置我们可以在如图位置找到

我们去最后一次构建结果查看日志文件

在结果查看构建结果是否成功

那么我们可以用python来写个读取结果的脚本,用jenkins来自动运行

import requests import json import urllib.request import urllib.error import time # 获取构建结果 def getResult(): fname = pathGitLab with open(fname, ‘rb’) as f: #打开文件 first_line = f.readline() #读第一行 #print (first_line) off = -50 #设置偏移量 while True: f.seek(off, 2) #seek(off, 2)表示文件指针:从文件末尾(2)开始向前50个字符(-50) lines = f.readlines() #读取文件指针范围内所有行 # print (lines) if len(lines)>=2: #判断是否最后至少有两行,这样保证了最后一行是完整的 last_line = lines[-1] #取最后一行 print (last_line) break off *= 2 if ‘FAILURE’ in last_line.decode() : return 1 # else: # return 0 #获取下一次构建的Number和当前构建的number def getNextNumber(): f = open(r’C:\Program Files (x86)\Jenkins\jobs\Developer\nextBuildNumber’, ‘r’) currentNumber=int(f.read())-1 return currentNumber #网络模块,用于企业微信发送信息 def jenkins(result): url=‘https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=2ef5c862-b2a6-45bc-a183-ee6666666666666666’ # 企业微信机器人的webhook if result==1: con={“msgtype”: “text”,“text”: {“content”: “站点构建提醒\r\n构建站点:Developer\r\n构建结果:FAILURE\r\n构建失败,请您检查代码并重新构建,谢谢”},} # else : # con={“msgtype”: “text”,”text”: {“content”: “developer构建结果:构建失败,请检查代码后重试!”},} jd=json.dumps(con).encode(‘utf-8’) req=urllib.request.Request(url,jd) req.add_header(‘Content-Type’, ‘application/json’) response=urllib.request.urlopen(req) if __name__ == ‘__main__’: jobCurrentNumber=getNextNumber() #获取当前构建number # print (jobCurrentNumber) # myDict=getDict()#获取同事所有联系方式 #获取当前构建的目录比如D:\Jenkins\jobs\gk_check\builds\153, path=“C:\\Program Files (x86)\\Jenkins\\jobs\\Developer\\builds\\”+str(jobCurrentNumber)+“\\” pathGitLab=path+“log” # 获取svn版本和id信息的文件路径 pathAuthor=path+“changelog.xml” # 获取递交者信息的文件路径 result=getResult() # 读取构建结果 # print (result) jenkins(result) # 最后执行函数 getResult()

这里的企业微信的webhook地址我们可以在如下的地方添加机器人

这样就可以获得webhook的地址来进行来进行企业微信提醒

接下来我们在jenkins的构建模块进行构建后操作

ping 127.1 -n 5 >nul # 延迟5秒,目的是的等jenkins构建完成来获取结果 @echo off D: cd D:\test start python developer_error.py #执行脚本

这里再简单说下curl 进行企业微信提醒

可以在构建最开始和结尾执行curl的命令

ios和Android的自动化构建结果也可以企业微信通知,如下图

还可以增加其他功能,等待大家去尝试,如果有什么问题或者创新想法大家可以说一哈