养一只”无限猴子”帮你测试

在上线之后发生了几次崩溃闪退, 需要紧急修复的情况之后, 我决定我要动手了...

分析了这几次情况之后, 发现其实大的逻辑都没有错, 但是细的东西特别容易出篓子, 例如说布尔条件写反了, 某个 @IBOutlet 的控件改名了, 删掉了, 忘了去 storyboard 里处理掉它, 就会发生 setValue: forUndefinedKey: 的错误, 本来我是想直接 swizzle 掉这个方法, 不让它抛出错误, 但是想想又觉得不值得. 难道终于要开始学一下怎么写测试了吗?

然后突然想起了之前好像看到过一个 UI 测试的框架, 可以自动帮忙测试 UI, 找到之后就开始用, 然后一发不可收拾.

仓库的位置在这里 GitHub - zalando/SwiftMonkey: A framework for doing randomised UI testing of iOS apps

简介

这个库让我想起了无限猴子理论, 其实也类似, 就是产生间隔一段事件就产生一个随机操作事件, 例如点击拖拽, 闪退的话是最容易发现的, 或者是你看到一些错误的数据和 UI 呈现.(效果图是张 gif, 手机端加载可能会比较慢)

68747470733a2f2f7468756d62732e6766796361742e636f6d2f496e646f6c656e7454616c6c466f78746572726965722d73697a655f726573747269637465642e676966.gif

这个库分成两部分:

  1. 主体是 SwiftMonkey, 依赖于 XCUITest, 调用了一些私有方法去发起操作事件
  2. SwiftMonkeyPaws, 负责呈现操作事件的视觉效果, 上面的动图里, 那些小手掌就是 SwiftMonkeyPaws 制造出来的, 需要直接接入到 app 里面

接入流程

官方文档目前还不是很详细, 我花了一点时间才把这个库给搞明白, 所以大概介绍一下接入流程.

包管理, 很简单嘛, 支持 Carthage 和 Cocoapods 两种方式, 想用哪个用哪个.

但使用 Cocoapods 的同学有一点事情要注意, 作者忘了 push podspec 到主仓库了, 所以我们 pod 里搜索和安装的都是 1.0.0 版本, 最低支持 iOS 9.0, 而最新的 1.0.1 版本最低支持 8.0.

Screen Shot 2017-03-16 at 8.34.06 PM.png

解决方法也很简单, pod 的时候指定仓库就行了, 就像这样:

pod 'SwiftMonkey', :git => 'https://github.com/zalando/SwiftMonkey.git'

安装完之后, 在 AppDelegate 里面我们需要初始化一下 SwiftMonkeyPaws, 有视觉效果毕竟会更好一点

import SwiftMonkeyPaws

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var paws: MonkeyPaws?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        #if DEBUG
            if CommandLine.arguments.contains("--MonkeyPaws") {
                paws = MonkeyPaws(view: window!)
            }
        #endif
        
        return true
    }
}

记得要在 AppDelegate 里声明一个 paws 去维持引用计数, 然后 MonkeyPaws 就会 swizzle 掉 UITouch 的方法, 让每次点击, 拖拽都会有相应的视觉效果.

这里我们看到一个 CommandLine.argments.contains(“—MonkeyPaws”) 可能会比较奇怪, 这段代码是为了区分开 app 是否跑在测试模式下的, 然后为了不在正式版里加入这段代码, 我们还加上了 compile flag 去判断是否编译这段代码. 直接加一个 ConfiguationSet 也行, 但不优雅, 也没必要...

接下里我们就去处理 UI 测试的代码:

import SwiftMonkey

class UITest: XCTestCase {
        
    override func setUp() {
        super.setUp()
        
        continueAfterFailure = false

        let app = XCUIApplication()
        app.launchArguments.append("--MonkeyPaws")
        app.launch()
    }
}

在 setup 方法里, 需要注意的就是最好把 continueAfterFailure 设为 false, 让代码出错时能够停留在出错的位置那里, 方便我们 DEBUG, 毕竟我们使用的不是常规的测试方法, 测试用例跟代码之间没有一一对应的关系.

还有一个就是加上参数 —-MonkeyPaws 去区分运行和测试状态, 不加的话 paws 就不会运行了.

那么久该开始写用例了, 我用的方式比较粗暴.

func testMonkey() {
    let application = XCUIApplication()

    // Workaround for bug in Xcode 7.3. Snapshots are not properly updated
    // when you initially call app.frame, resulting in a zero-sized rect.
    // Doing a random query seems to update everything properly.
    // TODO: Remove this when the Xcode bug is fixed!
    _ = application.descendants(matching: .any).element(boundBy: 0).frame

    let monkey = Monkey(frame: application.frame)
 
    monkey.addXCTestTapAction(weight: 25)
    monkey.addXCTestDragAction(weight: 200)
    monkey.addXCTestTapAction(weight: 100)
    monkey.addXCTestDragAction(weight: 30)
        
    monkey.monkeyAround(iterations: 360000)
}

前面的代码是我照抄官方给的例子的, 不加的话会有 bug.

接着我们初始化一只 Monkey, 然后给它添加一些动作, 其实还有什么各种 pinch, peek, pop 之类的, 但我的项目比较简单, 所以我就只加了点击和拖拽动作, weight 是间隔. monkeyAround 就是开始随机操作, iteration 是操作的次数, 操作满 360000 次就会停止.

这基本上就是我的用法, 里面还有一些挺有趣的东西, 之后有空的话我还会再探索一下, 例如添加多几个用例, 然后先跳转到新写的 ViewController 那里, 让这只猴子把里面的东西全都搞乱, 看看有啥 bug.

使用体验

到目前位置我用了这个库两三天, 每天中午去吃饭都会跑一下, 发现了几个 bug, 三个是低级错误, 两个比较隐晦, 主要是关于多次点击重复触发关键事件, 例如说一秒内连续点了七八次提交订单, 导致发出去七八个请求, 实际在网络情况不好的时候, 用户也有可能心急多次点击, 所以挺好的, 帮我提前预防了一些问题.

其实觉得无论是哪种情况, 都挺适合用一下这个库去找到一些低级的明显的 bug, 强烈推荐大家用一下.

觉得我写的还不错的话可以关注一下我的博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 作者设计思路 1.YYMemoryCache YYMemoryCache负责管理内存缓存。这个类是线程安全的。 L...
    WeiHing阅读 645评论 0 7
  • 设置快捷启动方式在Ubuntu下,每次都要找到 pycharm.sh所在的文件夹,执行./pycharm.sh,非...
    张不二01阅读 281评论 0 0
  • 光阴荏苒,转瞬间的流逝!人生一世,值得谨记于心的人与事能有多少!很多时候你会说:我自私!谁又会懂得自私的缘由!很多...
    承诺诠释人生阅读 255评论 0 0