基于XCUITest进行的iOSMonkey测试

本文主要用于介绍由于iOS与Xcode升级,之前很多iOS端的QA同学赖以生存UIAutomation框架被无情废弃,如何基于 XCUITesting进行monkey测试 ——SwiftMonkey。其实从Xcode升级到现在也有一年多了,这个工具现在网上的介绍也开始纷呈起来,但是还是打算记录一下自己的使用过程,主要是有些地方,在网上看到的很多资料都有问题后来自己混编了Swift源码,才搞清楚,所以希望本文还是能给摸索而来的tester们一点启示。
源码的GitHub地址: https://github.com/zalando/SwiftMonkey

总体介绍

先大概介绍一个monkey测试,简单说就是模拟一个monkey(淘气的猴子)的一种针对APP的随机测试,基本在功能验证bug收敛阶段引入,主要用于测试APP的稳定性,一般常见的crash闪退以及卡死问题都会影响APP的稳定性。
本文会在之前自学iOS时写的半废弃状态的练手APP天气预报作为demo做monkeyTest,首次使用可以直接拉代码实验,代码地址:git@github.com:gysxl/WeatherRequest.git
总体来说,在你的项目中添加monkey测试,主要分为以下几步:

  • 添加monkey工具源码
  • 新建并配置UI testing target
  • 编写测试代码
  • run monkey并分析BUG日志
  • 锦上添花添加手势记录

添加monkey工具源码

前言已经提到过,本文所用到的主要是一个基于 XCUITesting 框架的 monkey 工具 —— https://github.com/zalando/SwiftMonkey ,所以首先肯定要将人家的源码down下来,PS:能翻墙最好翻个墙,干啥都能快一些,也不会遇到什么乱七八糟的错误。
下载下来后,需要把目录中的SwiftMonkey与SwiftMonkeyPaws文件复制到你的项目目录中,参考我下图的文件结构:

然后把这两个项目的.xcodeproj拖到你的项目中,如下所示:

以上代码拖入的工作就完成了,这个是比较直观的拖入操作,其实也可以通过pod集成的方式直接导入,直接通过修改pod库文件,修改部分如下(具体的根据自己的项目来调整,不过表示我还没尝试这种方法,只是先普及一下):

1
2
3
4
5
6
7
target "App" do //App为自己的工程名
pod "SwiftMonkeyPaws", "~> 1.0"
end
target "Tests" do //Test为text对应的Target名
pod "SwiftMonkey", "~> 1.0"
end

然后通过 pod install 的方式集成

新建并配置UI testing target

现在项目中添加一个新的target,依次选择file——>new——>target,最后选择iOS UI testing Bundle:

点击next后就配置一些target的信息,这里需要注意的是,由于这个工具是用swift写的,所以需要选择的语言是Swift,其他的参考下面的配置然后根据个人的项目修改就可:

配置相关的target信息:


需要注意的是,由于工具是用Swift编写的,所以需要在项目中的build setting中勾选上Swift的标准库,否则不能集成工具库,而且新建的target需要注册成功,勾选你的证书什么的注册一下就好。如下图所示:


以上就配置完成了!

编写测试代码

打开你自己创建的Swift代码,我的项目中,文件名为:MonkeyForWeatherRequestUITests.swift,将里面的的内容替换为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import XCTest
import SwiftMonkey
class MonkeyForOCUIMonkeyTests: XCTestCase {
override func setUp() {
super.setUp()
XCUIApplication().launch()
}
override func tearDown() {
super.tearDown()
}
func testMonkey() {
let application = XCUIApplication()
_ = application.descendants(matching: .any).element(boundBy: 0).frame
let monkey = Monkey(frame: application.frame)
monkey.addDefaultXCTestPrivateActions()
monkey.addXCTestTapAlertAction(interval: 100, application: application)
monkey.monkeyAround()
}
}

上面基本上是SwiftMonkey的默认代码,addDefaultXCTestPrivateActions是调用Apple私有手势方法,addDefaultUIAutomationActions只在模拟器中有效,内部实现如下:

1
2
3
4
5
6
7
8
9
public func addDefaultXCTestPrivateActions() {
addXCTestTapAction(weight: 25)
addXCTestLongPressAction(weight: 1)
addXCTestDragAction(weight: 1)
addXCTestPinchCloseAction(weight: 1)
addXCTestPinchOpenAction(weight: 1)
addXCTestRotateAction(weight: 1)
//addXCTestOrientationAction(weight: 1) // TODO: Investigate why this does not work.
}

默认执行点击,长按,拖拽,捏合,旋转,横竖屏切换操作,Weight代表的是时间间隔,如果觉得系统默认的操作过多,可以自行删减.

run monkey并分析BUG日志

编写完测试代码后,整个monkey的集成工作就完成了,接下来就可以跑你的App了,点击下图所示的这个小按钮,程序就开始了乱点的Monkey过程。

跑起来的感觉可以盗用官方的视频展示一下:

具体的结果分析,后续会写几个标准的crash类型去测试并分析。敬请期待,后续继续完善此文~~~

锦上添花添加手势记录

最上面说到过把git下来的SwiftMonkeyPaws拖到我们的项目中,但是在上述的使用中并没有提到关于这个项目的说明,其实名字就很容易看出来paws是手掌的意思,显而易见,就是用手掌点击的方式把monkey随机的操作在界面上显示出来,最后的效果就想上面引用的官方的那个动图中的小手掌的样式。想要在oc项目中加上这个小手势会遇到一个oc代码引入swift方法的问题,这就需要用到“桥接”这一概念–本文会介绍一个简单的创建桥接文件的方式。

  • OC与Swift混编之桥接文件
    Xcode有个比较让人暖心的做法,就是在每个项目中第一次创建不同语言的文件时,会自动提示是否新建桥接文件。所以我们首先在现有的项目中新建一个Swift文件,名字随意,主要用于自动创建桥接文件。

    创建成功,点击next后,会自动弹出添加桥接文件的提示:

    选择Create Bridging Header,此时你会发现项目会出现一个 项目名-Bridging-Header.h的文件,这个就是桥接文件了,然后在自己的桥接文件中引入需要的类库:

    以上,oc与Swift的桥接文件就完成了!!!
    在需要使用到Swift类的oc文件中,引入如下的头文件:
    1
    2
    #import "WeatherRequest-Swift.h"
    #import "WeatherRequest-Bridging-Header.h"

然后就用畅通无阻的使用了。

  • 在oc代码中引入手掌
    在自己的项目的 Appdelegate 中代码实现如下内容:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #import "WeatherRequest-Swift.h"
    #import "WeatherRequest-Bridging-Header.h"
    @interface AppDelegate ()
    @property (strong, nonatomic) MonkeyPaws *paws;
    @end
    @implementation AppDelegate
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    ViewController *homeViewController = [[ViewController alloc] init];
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:homeViewController];
    self.window.rootViewController = navigationController;
    self.paws = [[MonkeyPaws alloc] initWithView:self.window tapUIApplication:true]; //add for SwiftMonkeyPaws
    return YES;
    return YES;
    }

添加完成后,再次按照第四步的方式run起来monkey就是上图那种带小手手的monkeyTest了。

  • 控制monkey循环
    monkeyAround代表执行时间,单位是s,默认如果不设置时间,会一直执行下去.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /// - Parameter duration: The duration for which to generate the random events.
    /// Set to `.infinity` by default.
    public func monkeyAround(forDuration duration: TimeInterval = .infinity) {
    let monkeyTestingTime = Date().timeIntervalSince1970
    repeat {
    actRandomly()
    actRegularly()
    } while ((Date().timeIntervalSince1970 - monkeyTestingTime) < duration)
    }

func testMonkey中设置如下控制monkey时间

1
2
3
4
5
6
7
// 如果指定时间,请调用这个函数
monkey.monkeyAround(duration: 3000)
// 如果不指定次数,将一直运行测试
monkey.monkeyAround()