查看连接的设备

  1. C:\Users\zengy>adb devices
  2. List of devices attached
  3. 127.0.0.1:62001 device

文件的上传和下载

上传命令

将Windows桌面上的dog.png 上传到手机的 /sdcard/DCIM 目录中

  1. C:\Users\zengy>adb push C:\Users\zengy\Desktop\dog.png /sdcard/DCIM
  2. C:\Users\zengy\Desktop\dog.png: 1 fi.... 16.9 MB/s (270575 bytes in 0.015s)

截屏

  1. adb shell screencap /sdcard/DCIM/01.png

将手机当前屏幕截屏文件放到 /sdcard/DCIM/01.png

文件下载

将手机文件 /sdcard/DCIM/01.png 复制到Windows 桌面

  1. C:\Users\zengy>adb pull /sdcard/DCIM/01.png C:\Users\zengy\Desktop
  2. /sdcard/DCIM/01.png: 1 file pulled, .... 47.0 MB/s (562604 bytes in 0.011s)

手机端录屏

  1. adb shell screenrecord /sdcard/demo.mp4

按 Ctrl + C 键(在 Mac 上,按 Command + C 键)可停止屏幕录制;如果不手动停止,到三分钟或 —time-limit 设置的时间限制时,录制将会自动停止。

monkey命令

基本使用

  1. adb shell monkey 10000

monkey 主要是用来做Android客户端的稳定性测试, monkey 可以随机模拟出一系列伪事件流。
移动端app 中最严重的bug是 程序崩溃(crash)或者是程序未响应(ANR)。
通过Monkey可以生成伪随机事件流来检查手机客户端是否有这样的问题。

查看app package 信息

  1. adb shell pm list packages -3

image.png

monkey 运行指定app

  1. adb shell monkey -p com.sina.weibo 3000
  • -p 指定运行包

指定运行 微博应用 执行事件 3000

运行日志

  1. adb shell monkey -p com.sina.weibo -vv 3000
  • -vv 显示详细的运行日志

指定运行速度

  1. adb shell monkey --throttle 500 -p com.sina.weibo -vv 100

—throttle 500 事件之间的间隔时间 500毫秒。

将运行日志保存到指定文件。

  1. adb shell monkey --throttle 500 -p com.sina.weibo -vv 100 >C:\Users\zengy\Desktop\monkeylog.txt

练习

app.zip
解压zip文件,使用apk文件进行如下操作

  1. 下载app 使用adb 命令安装
  2. 查看此应用的包名
  3. 使用 monkey命令运行 100次, 运行完成之后 将运行日志保存到 xxxx.log 文件中

  1. C:\Users\zengy>adb install -r C:\Users\zengy\Desktop\app.apk
  2. Performing Streamed Install
  3. Success
  4. C:\Users\zengy>adb shell pm list packages -3
  5. package:io.appium.settings
  6. package:com.chaoxing.mobile
  7. package:com.wstl.w.csw
  8. package:io.appium.uiautomator2.server
  9. package:com.jingdong.app.reader
  10. package:org.cnodejs.android.md
  11. package:com.perfdog.app
  12. package:com.umarkcloud.chaoyang
  13. package:com.ss.android.ugc.aweme
  14. package:io.appium.uiautomator2.server.test
  15. package:com.fanmao.crashtest
  16. package:com.app.yujiasuxingji
  17. package:com.sina.weibo
  18. package:bubei.tingshu
  19. package:com.eg.android.AlipayGphone
  20. C:\Users\zengy>adb shell monkey -p com.umarkcloud.chaoyang --throttle 500 -vv 100 > xxxx.log

monkey支持的事件

事件 -s 伪随机数生成器的种子值。如果您使用相同的种子值重新运行 Monkey,它将会生成相同的事件序列。
—throttle 在事件之间插入固定的延迟时间。您可以使用此选项减慢 Monkey 速度。如果未指定,则不延迟,系统会尽快地生成事件。
—pct-touch 调整轻触事件所占百分比。(轻触事件是指屏幕上的单个位置上的按下/释放事件。)
—pct-motion 调整动作事件所占百分比。(动作事件包括屏幕上某个位置的按下事件,一系列伪随机动作和一个释放事件。)
—pct-trackball 调整轨迹球事件所占百分比。(轨迹球事件包括一个或多个随机动作,有时后跟点击。)
—pct-nav 调整“基本”导航事件所占百分比。(导航事件包括向上/向下/向左/向右,作为方向输入设备的输入。)
—pct-majornav 调整“主要”导航事件所占百分比。(这些导航事件通常会导致界面中的操作,例如 5 方向键的中间按钮、返回键或菜单键。)
—pct-syskeys 调整“系统”按键事件所占百分比。(这些按键通常预留供系统使用,例如“主屏幕”、“返回”、“发起通话”、“结束通话”或“音量控件”。)
—pct-appswitch 调整 Activity 启动次数所占百分比。Monkey 会以随机间隔发起 startActivity() 调用,以最大限度地覆盖软件包中的所有 Activity。
—pct-anyevent 调整其他类型事件所占百分比。这包括所有其他类型的事件,例如按键、设备上的其他不太常用的按钮等等。

轻触事件 60%
系统事件 10%
其它事件 30%

  1. adb shell monkey -p com.umarkcloud.chaoyang --throttle 500 -vv --pct-touch 60 --pct-syskeys 10 1000

monkey的种子值

因为monkey 每次执行都是生成新的随机事件。如果使用monkey 运行过程中将手机app 运行崩溃。 每次monkey执行的时候都会生成唯一的一个种子值。
image.png

运行的时候指定种子值,可以生成相同的随机事件序列。

  1. adb shell monkey -p com.umarkcloud.chaoyang -s 1629730450295 --throttle 500 -vv --pct-touch 60 --pct-syskeys 10 100
  • -s 1629730450295 指定种子值。

这个种子值是上次运行过程中产生的。指定种子可以生成与上次生成一样事件序列。

monkey 命令运行发现bug

正常运行完成之后,对应的日志
image.png
如果monkey执行过程应用crash 崩溃,对应的日志
image.png
有对应的说明以及种子值。

如果app 崩溃,将对应的日志发给开发

  1. C:\Users\zengy>adb shell monkey -p com.fanmao.crashtest 100
  2. // CRASH: com.fanmao.crashtest (pid 8949)
  3. // Short Msg: android.content.ActivityNotFoundException
  4. // Long Msg: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.example.app.mainactivity2 }
  5. // Build Label: samsung/dream2qltezh/dream2qltechn:7.1/N2G48H/G9550ZHU1AQEE:user/release-keys
  6. // Build Changelist: 700210421
  7. // Build Time: 1619015543000
  8. // android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.example.app.mainactivity2 }
  9. // at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1809)
  10. // at android.app.Instrumentation.execStartActivity(Instrumentation.java:1523)
  11. // at android.app.Activity.startActivityForResult(Activity.java:4238)
  12. // at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:675)
  13. // at android.app.Activity.startActivityForResult(Activity.java:4196)
  14. // at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:662)
  15. // at android.app.Activity.startActivity(Activity.java:4535)
  16. // at android.app.Activity.startActivity(Activity.java:4503)
  17. // at com.fanmao.crashtest.MainActivity$1.onClick(MainActivity.java:22)
  18. // at android.view.View.performClick(View.java:5638)
  19. // at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:992)
  20. // at android.view.View.onKeyUp(View.java:10662)
  21. // at android.widget.TextView.onKeyUp(TextView.java:6851)
  22. // at android.view.KeyEvent.dispatch(KeyEvent.java:2712)
  23. // at android.view.View.dispatchKeyEvent(View.java:9961)
  24. // at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1630)
  25. // at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1630)
  26. // at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1630)
  27. // at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1630)
  28. // at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1630)
  29. // at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1630)
  30. // at com.android.internal.policy.DecorView.superDispatchKeyEvent(DecorView.java:406)
  31. // at com.android.internal.policy.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1798)
  32. // at androidx.core.view.KeyEventDispatcher.activitySuperDispatchKeyEventPre28(KeyEventDispatcher.java:130)
  33. // at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:87)
  34. // at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:140)
  35. // at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:569)
  36. // at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59)
  37. // at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:3054)
  38. // at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:320)
  39. // at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:4331)
  40. // at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4302)
  41. // at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
  42. // at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
  43. // at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
  44. // at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3999)
  45. // at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
  46. // at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4056)
  47. // at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
  48. // at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
  49. // at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
  50. // at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
  51. // at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
  52. // at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
  53. // at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
  54. // at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4032)
  55. // at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:4193)
  56. // at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2428)
  57. // at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:1992)
  58. // at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:1983)
  59. // at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2405)
  60. // at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
  61. // at android.os.MessageQueue.nativePollOnce(Native Method)
  62. // at android.os.MessageQueue.next(MessageQueue.java:323)
  63. // at android.os.Looper.loop(Looper.java:136)
  64. // at android.app.ActivityThread.main(ActivityThread.java:6198)
  65. // at java.lang.reflect.Method.invoke(Native Method)
  66. // at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:891)
  67. // at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:781)
  68. //
  69. ** Monkey aborted due to error.
  70. Events injected: 48
  71. ## Network stats: elapsed time=1289ms (0ms mobile, 0ms wifi, 1289ms not connected)
  72. ** System appears to have crashed at event 48 of 100 using seed 1629887510786

面试问题

  1. 说出几个你常用的 adb 命令。

    1. adb devices
    2. adb push
    3. adb pull
    4. adb shell
    5. adb shell pm list packages
    6. adb install
    7. adb uninstall
    8. adb shell monkey
  2. 开发是怎么给你分发测试客户端的?

android 客户端的文件后缀名 .apk, 可以直接安装到手机上。
iPhone客户端的文件后缀名 .ipa, 不能直接安装到手机,由于苹果的安全性限制,不能直接安装到手机。


公司中常用内测版本分发平台: https://www.pgyer.com/ (蒲公英)
公司开发开发好客户端 上传到 蒲公英平台,
开发发送邮件给到测试人员,邮件中会有一个 app 文件的链接地址。
测试人员只需要在手机上打开这个url 就可以自动安装。

  1. 手机app的稳定性测试是如何做的?

平时使用monkey命令进行测试
monkey 命令参考上面文档。
注意:monkey命令 不能操作IOS系统。

  1. 平时怎么做兼容性测试?

可以使用云平台进行兼容性测试,比如腾讯的wetest平台。
公司内部也有测试机,adb 命令进行安装卸载测试,主要测试在不同的版本,不同系统上能够正常安装和卸载。
主要考虑:
Android设备
1 . Android不同版本:
Android 6.0
Android 7.0
Android 8.0
Android 9.0
Android 10.0
Android 11.0
当前最新的版本 Android 12.0 但是还没有正式发布。

  1. 机型以及分辨率

高端机
中端机
低端机型。

IOS 设备
机型和系统版本。