本文由 伯乐在线 - nathanw 翻译,dopcn 校稿。未经许可,禁止转载! 英文出处:www.mokacoding.com。欢迎加入翻译小组

几周前,我决定将将我在 mokacoding 上的创作更多集中在单元测试与验收测试,自动化和生产效率上,主要在iOS领域。

相关深入文章可以看看“通过 CocoaPods 为 iOS 项目创建 Calabash 并构建配置”和“用终端运行 Xcode 测试”。

这周我们要回过头来看看,或者说是站在更高的角度审视单元测试和验收测试,以及在云端运行持续集成有哪些资源。

就像有人创建 walking skeleton 时会做的事情一样,我们也将先查看 Cocoa 和 Xcode 提供给开发者的工具,然后再看看能实现更好效果的开源库,最后整理出在云端持续集成环境运行测试的解决方案。

Xcode,测试开始的地方

伴随着 iOS 7 和 Xcode 5,苹果发布了 XCTest,一个简单而又强大的测试编写框架,使用了同 xUnit 一样的风格。

编写 XCTest 测试很简单,开发者在 Xcode 点击 ⌘U 运行测试便能持续不断地迅速获得反馈。

Xcode 还有一个叫“Test Navigator”的界面,它可以让我们看到所有测试点,包括在最后一次运行后的成功或失败状态。

值得注意的是,红色为测试失败,绿色为测试通过。在不断迭代过程中颜色会给你很大帮助。

XCTest 已经高度集成在 Xcode 中,使用简单方便。这是它主要优点,也是缺点。XCTAssert 类 API 并不容易理解,也不灵活。从 Xcode 外边运行测试也没有你想象的那么简单。

在过去两年中,iOS 和 OS X 的单元测试框架已经变得越来越好,而验收测试这边反而没什么进步。

苹果提供了 UIAutomation 框架来编写 UI 自动化测试。UIAutomation 测试使用 javascript 写成。允许用户使用代码驱动应用 UI 并给它的状态设置断言。尽管看上去很美好,使用 UIAutomation 其实是很繁琐的, javascript API 也没有原生代码写成的单元测试那样强大。

这是 UIAutomation 测试的一个小片段。

1
2
3
4
5
UIATarget.localTarget().frontMostApp().navigationBar().buttons()["Add"].tap();
 
UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].cells()[0].elements()["Chocolate Cake"];
 
UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].scrollToElementWithPredicate("name beginswith 'Turtle Pie'");

你可以看到的,javascript API 比 Foundation 中的那些更加冗长。再加上这种测试需要在 Instruments 中运行,你就可以想象使用这个框架是多么的不爽。

最后是苹果的 CI 解决方案:Xcode Bots。我们可以配置一个 Xcode Bot,在需要的时候触发他工作,例如运行我们的测试,Xcode Bots 可以存放在服务器端。

我承认我自己并没有用过 Xcode Bots,但是我获得的所有反馈都告诉我这个东西并不好用。

总结下,如今缺乏好奇心的开发者和大公司,可以只使用苹果的技术,组建一整套运行在CI的单元测试和验收测试。用于工作基本上是足够了。

如果你正在阅读本文,你可能充满了好奇心,那么让我们继续看看开源社区有那些资源。

开源单元测试框架

iOS 和 OS X 开源社区充满了各种大牛和有趣的项目。在写本文的时候,在 pod 上一共有 8625 个开源项目。

这些单元测试的开源库主要都是行为描述风格(xSpec),一定程度上也反映了测试风格的一种趋势,这风格来自于 Ruby 测试库的 RSpec, 主要是测试类的行为,而不是枚举方法。

Kiwi

Kiwi 是一个全栈式的,XCTest的代替品,支持行为描述句式。实例代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
describe(@"Team", ^{
  context(@"when newly created", ^{
    it(@"has a name", ^{
      id team = [Team team];
      [[team.name should] equal:@"Black Hawks"];
    });
 
    it(@"has 11 players", ^{
      id team = [Team team];
      [[[team should] have:11] players];
    });
  });
});

Kiwi 测试用例通常非常容易阅读和理解代码所想要测试的内容,他就像一个好的说明文档。

Kiwi 集成了一些测试方法 期望(expectations), 模拟对象 (mock),桩程序 (stub),甚至还支持异步测试

Specta

Specter 跟 Kiwi 非常像,但是它使用了不同的架构。Kiwi 是庞大的代替品 ,Specta 优势则体现在模块化与组件化。这个库关心的唯一事情是编写和运行 xSpec 风格的测试,然后用户可以根据使用期望(expectations), 匹配(matching),模拟对象(mock)和桩程序(stub)的情况来补充相应模块。

我个人更喜欢这个库的设计,轻量级,包含的多个模块可以被结合在一起。

这是 Specta 行为描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SpecBegin(Thing)
 
describe(@"Thing", ^{
  it(@"should do stuff", ^{
    // This is an example block. Place your assertions here.
  });
 
  it(@"should do some stuff asynchronously", ^{
    waitUntil(^(DoneCallback done) {
      // Async example blocks need to invoke done() callback.
      done();
    });
  });
});

注意 it 执行的时候 blocks 是空的。留给库的使用者来用他们喜欢的工具填写。

说到工具,这里有一个库名单,他们都可以与 Specta 和 Kiwi 配合使用:

  • Expecta a matcher framework, expect(foo).to.equal(bar).
  • OCHamcrest another matcher framework, assertThat(foo, equalTo(bar)).
  • OCMock a mocking framework.
  • OCMockito another mocking framework.
  • OHTTPStubs a library to stub network requests, with block based syntax to match URLs.
  • Nocilla another library to stub network requests, with a nice chain-able API, stubRequest(@”POST”, ).withHeaders(…).withBody(…).

Quick

Quick 是一个新的测试框架,也相当炫酷的一个。主要代码都是用 Swift 写的,非常适合用新的语言写测试组件。

1
2
3
4
5
6
7
8
9
10
11
import Quick
 
class ThingSpec: QuickSpec {
  override func spec() {
    describe("a 'Thing'") {
      it("should do stuff) {
        //
      }
    }
  }
}

多亏了 Swift 的语法和闭包,Quick 的行为描述看起来比 Kiwi 和 Specta 的可读性更强。

和 Qucik 一起的 Nimble 是一个 matcher 库,它允许用户进行简洁地表达,例如 expect(10) > 2

无论是 Objective-C 还是 Switf,单个庞大框架或是你喜欢的库组成的组件,开源社区提供了大量有价值的测试框架,特别是专注于写简洁测试的,感谢有表达句法(expressive syntax)。

验收测试的开源库

苹果提供的官方工具中单元测试框架和验收测试框架的质量对比也反应在开源社区中。可能是因为 XCTest 为开源单元测试框架们提供了一个坚实的基础,而 UIAutomation 没有,所以我们只能选择一些非常规的方法。

KIF

KIF,保持函数式(Keep It Functional),这是一个用 Objective-C 写的框架,让我们使用 XCTest 编写验收测试,然后在 Xcode 运行,方式和我们在单元测试做的一样。

KIF 使用私有的 API 来获得视图层级,然后让我们使用 accessibility 标签值来视图查询与交互。

1
2
3
4
5
6
7
8
- (void)testSuccessfulLogin {
  [tester enterText:@"user@example.com" intoViewWithAccessibilityLabel:@"Login User Name"];
  [tester enterText:@"thisismypassword" intoViewWithAccessibilityLabel:@"Login Password"];
  [tester tapViewWithAccessibilityLabel:@"Log In"];
 
  // Verify that the login succeeded
  [tester waitForTappableViewWithAccessibilityLabel:@"Welcome"];
}

KIF比较不好的地方在于作者响应时间较慢。这不是批判,毕竟开源世界一切都是免费的,但我们都要赚钱糊口,可以理解作者用在这些项目上的时间是有限的。但是当整个框架的基础都非常难以使用,那么他的稳定性一定很低。

Subliminal

Subliminal 是一个类似 KIF 的 Objective-C 框架,集成了 XCTest。和 KIF 不同的是,SUbliminal 是写在 UIAutomation 上层,旨在为开发者隐藏它的复杂性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (void)testLogInSucceedsWithUsernameAndPassword {
  SLTextField *usernameField = [SLTextField elementWithAccessibilityLabel:@"username field"];
  SLTextField *passwordField = [SLTextField elementWithAccessibilityLabel:@"password field" isSecure:YES];
  SLElement *submitButton = [SLElement elementWithAccessibilityLabel:@"Submit"];
  SLElement *loginSpinner = [SLElement elementWithAccessibilityLabel:@"Logging in..."];
 
  NSString *username = @"Jeff", *password = @"foo";
  [usernameField setText:username];
  [passwordField setText:password];
 
  [submitButton tap];
 
  // wait for the login spinner to disappear
  SLAssertTrueWithTimeout([loginSpinner isInvalidOrInvisible], 3.0, @"Log-in was not successful.");
 
  NSString *successMessage = [NSString stringWithFormat:@"Hello, %@!", username];
  SLAssertTrue([[SLElement elementWithAccessibilityLabel:successMessage] isValid],
  @"Log-in did not succeed.");
 
  // Check the internal state of the app.
  SLAssertTrue(SLAskAppYesNo(isUserLoggedIn), @"User is not logged in.")
}

Subliminal 声明它可以测试应用内购警告,甚至能使 app 进入睡眠。这听起来很牛,但事实是,在我写本文的时候,该库最近的一次代码提交是 2014年9月,而且还有 13 活跃的 pull request,这些都是不好的信号。

Calabash

目前我们所说到的工具中,Calabash 是最原始的一个。它是一个 Ruby 包,使用 Cucumber 编写 BDD 风格的验收测试,现在由 Xamarin 维护。Xamarin 是一个用 C# 写 iOS 和 Android 应用的框架。语言会不会有点多!

不像 KIF 和 Subliminal,Calabash 完全不集成在 Xcode 中。我创建示例使用的是 Vim 和 Rake

我们书写 Cucumber 特性,执行每一步,然后使用命令行测试。为了它能够工作,需要在应用内嵌入一个 HTTP 服务器,用于查询和驱动 UI。

不用说,这可能是一个很大坑。

Cucumber/Calabash 测试代码差不多是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# rating_a_stand.feature
 
Feature: Rating a stand
  Scenario: Find and rate a stand from the list
    Given I am on the foodstand list
    Then I should see a "rating" button
    And I should not see "Dixie Burger & Gumbo Soup"
 
# steps.rb
 
Given(/^I am on the foodstand list$/) do
  wait_for_element_exists "view marked:'Foodstand'"
end
 
Given(/^I should see a "([^"]*)" button$/) do |button_title|
  wait_for_element_exists "button marked:'#{button_title}'"
end
 
Given(/^I should not see "([^"]*)"$/) do |view_label|
  wait_for_element_does_not_exists "view marked:'#{view_label}'
end

Calabash 好的地方在于它是一种陈述式的测试,管理层会喜欢如果他们会读到这些测试的话。而且它可以兼容两个平台。

另一方面,工具链并不是非常强大。测试运行相对较慢,需要在 Cucumer,Ruby,Objective 之间持续交换,消耗相当多的时间。

就像单元测试,开源库提供了不同的选择,用于改进你的工作流。唯一不同的是这些工具没那么成熟,社区没那么活跃。

持续集成平台

为我们的项目套上好的测试工具,其最后一步是拥有持续集成。在开发者机器上运行测试并不能保证代码不会出错,毕竟其他团队成员会对代码进行更改。有个人来不断运行测试会更加安全。

不用说,最好的 CI 是在云端进行。配置维护一套 Jenkins 需要大量的时间。

CI 的选择会更多。这里列出一些支持 iOS 项目的主要 CI 服务。

它们之间的区别主要是在价格,上手容易程度,以及如何配置。例如 Travis CI 使用 .travis.yml文件定义所有的步骤,而 Bitrise 则图形界面,每个步骤都用 block 展示,并且这block可以被添加到进程。

上面这个列表可能并不全面,我可能落了一些。希望这个对于有兴趣写测试和 CI 的人是一个好的开始。

2015年iOS测试现状的更多相关文章

  1. iOS 测试在应用发布前后的痛点探索以及解决方案

    作者-芈 峮 前言 iOS 开发从 2010 年开始在国内不断地升温,开发和测试相关的问题不绝于耳.iOS 测试主要涉及哪些内容?又有哪些挑战呢?带着疑问我们开始第一个大问题的讨论. iOS 测试的范 ...

  2. 使用appium进行ios测试,启动inspector时遇到的问题(一)

    最近在公司,让做ios的自动化测试,因为以前做过android的自动化测试,用的也是appium,觉得没什么,结果一开始在搭建环境就遇到了很多的问题,现在将我遇到的问题,以及解决方法,给大家分享出来. ...

  3. 如何利用Pre.im分发iOS测试包

    大众创新万众创业,在移动互联网的风口,移动APP开发与测试发展方兴未艾,受到了越来越多的重视.相较 iOS,Android 的开发环境更加开放.Android 开发者要测试应用时,只需发个 APK 安 ...

  4. 【读书笔记】iOS-软件测试与iOS测试

    一,软件测试的类型. 1.软件测试按照测试类型,可以划分为:单元测试,集成测试和系统测试. 2.单元测试是指对软件系统中最小可测试单元进行的检查和验证. 3.集成测试,在iOS软件开发中,集成测试主要 ...

  5. iOS测试中发现一个textview控制,使用clear()无法清除文字

    iOS测试中发现一个textview控制,使用clear()无法清除

  6. 经典软件测试面试题目:Android 和 ios 测试区别?这样回答:稳!

    Android 和  ios 测试区别? App 测试中 ios 和 Android 有哪些区别呢?1.Android 长按 home 键呼出应用列表和切换应用,然后右滑则终止应用:2.多分辨率测试, ...

  7. 【iOS测试】【随笔】帧率FPS评测

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/5943263.html   背景 我们的新版App对首页的列 ...

  8. iOS测试常见崩溃

    什么是崩溃日志,从哪里能得它? iOS设备上的应用闪退时,操作系统会生成一个崩溃报告,也叫崩溃日志,保存在设备上.崩溃日志上有很多有用的信息,包括应用是什么情况下闪退的.通常,上面有每个正在执行线程的 ...

  9. 免费安卓IOS测试API接口,后续会陆续增加接口

    各位博友好!开发的安卓或者ios的朋友们,经常会遇到想测试但是没有公开的api接口进行进行测试.但自己又不会开发服务端或者没有服务器,这里我免费提供了一整套API接口.欢迎大家调用,目标是方便大家. ...

随机推荐

  1. C++断言与静态断言

    断言是很早之前就有的东西了,只需要引入cassert头文件即可使用.往往assert被用于检查不可能发生的行为,来确保开发者在调试阶段尽早发现“不可能”事件真的发生了,如果真的发生了,那么就表示代码的 ...

  2. 开源项目:windows下使用MinGW+msys编译ffmpeg

    本文参考了网络上的不少文章,但由于版本环境的问题参考文章并不能直接指导编译,本文吸收多方经验,并在自己多次编译实验的基础上写成,欢迎转载,请注名出处.    FFmpeg是在Linux平台下开发的,但 ...

  3. iOS开发中view controller设置问题

  4. Magento修改css样式更新之——grunt命令使用

    1.清除pub/static和var中相应文件 2.源头文件重新导入pub/static 3.pub中的less编译 4.字面翻译是跟踪源头文件变化实时编译,但是这里的the source files ...

  5. javascript 详解数组

      概念 数组创建 数组读写 数组 VS. 一般对象 相同点 不同点 稀疏数组 数组的length属性 元素增删 数组迭代 二维数组 数组方法 Array.prototype.join Array.p ...

  6. MariaDB的双主

     MariaDB(mysql)之主主复制   一.主主复制说明    MySQL主主复制结构区别于主从复制结构.在主主复制结构中,两台服务器的任何一台上面的数据库存发生了改变都会同步到另一台服务器上, ...

  7. jQuery通过jquery.form.js插件使用AJAX提交Form表单

    我简单使用了一下,jQuery Form插件有一下优点:  1.支持提交前验证. 2.支持提交后回调. 3.采用AJAX方式,有很好的用户体验 4.提交方式是灵活.只要指定要提交的form ID即可. ...

  8. C#路径 (转载)

    一.获取当前文件的路径1.   System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName     获取模块的完整路径,包括文 ...

  9. 学习c语言的第9天

    #include <stdio.h> int main() { float sum=0,wage=0; int i=1; int num; printf("+++平均工资统计程序 ...

  10. zabbix一些高级功能介绍

    根据上篇配置的环境,接下来说明在zabbix agent上执行远程命令是如何完成的. 远程命令受到一些限制: (1)在agent执行远程命令必须给zabbix用户定义sudu规则: (2)不支持act ...