简单介绍:

单元測试是软件开发的一个重要方面。毕竟,单元測试能够帮你找到bug和崩溃原因,而程序崩溃是Apple在审查时拒绝app上架的首要原因。

单元測试不是万能的,但Apple把它作为开发工具包的一部分,不仅让你创作的APP更稳定,并且提供了一致、有趣的用户体验,这些都是让用户给你五星评价的源泉。iOS7提供了一个升级的单元測试框架。让你在Xcode中执行单元測试更为easy。当你完毕这一章节,你将学会怎样给现有app加入測试——并有可能培养出对编写測试的热爱!

/*

本文翻译自《iOS7 by Tutorials》一书的第十一章“Unit Testing in Xcode 5”,想体会原文精髓的朋友请到Raywenderlich商店支持正版。

——————(博客园、新浪微博)葛布林大帝

*/

文件夹:

一、单元測试基础

二、開始项目

三、下一步何去何从?

四、挑战

附录:XCTest断言參考

一、单元測试基础

在过去。Xcode引入了一个叫做OCUnit的开源单元測试框架。而在Xcode 5里面。Apple公布了他们自己的的单元測试框架,叫做XCTest。

假设你已经熟悉OCUnit。别操心,XCTest是一个建立在OCUnit之上而且十份相似的API。

从OCUnit过渡到XCTest很easy。要做的不过把STFail替换为XCTFail、STAssert替换为XCTAssert等等诸如此类。假设你已经熟悉这些基础,能够直接跳到下一节。

1.高层次概述

单元測试有四个层级。从上到下,它们各自是:

  • 測试套件(Test suite)

    • 这是项目里測试的所有集合。在Xcode里,測试套件被设置为一个单独的build target。
  • 測试用例类(Test case classes)
    • 正如你可能所期待的,在一个面向对象体系中。測试被整合到类里。

      在你的app里,每一个測试类通常相应一个单独类。比如,DeveloperTests类应该相应Developer类的測试。

  • 測试用例方法(Test case methods)
    • 每一个測试类包括多种方法。用来測试类的各种功能。就像一个方法或函数应该既精简又有用,每一个測试用例应该測试一个特定的结果——而且全然測试。

  • 断言(Assertions)
    • 断言检查相应预期结果的详细条件。假设条件不符合预期结果, Xcode会报错指出断言失败。比如,能够断言你的Developer 类响应“writeKillerApp: message”;假设它没有。断言失败,Xcode报错。

理论非常美好。但有时举例会更easy阐述事物。用Empty Application 模板创建一个新项目,命名为EmptyApp。 Xcode模板会自己主动包括一个叫做EmptyAppTests的test target,加入到EmptyApp的 app target里,例如以下图:

注意測试用例类包括了一个没有关联头文件的.m文件。打开EmptyAppTests.m看看第一个測试用例的源码。

測试方法必须以单词test開始,以便test runner能找到它们。

在你的演示样例项目里。測试类包括了一个測试方法,叫做testExample

setUptearDown方法就像守护在測试用例周围的卫兵一样。

把全部对象的程序设置代码或反复性代码放到setUp里。使測试用例方法保持清爽、高效。

类似的,关闭文件句柄或取消挂起网络请求等清理活动的方法应该放到tearDown里。

Test runner 会依次调用setUp、testExample和tearDown方法。假设你申明了第二个測试方法testSecondExample。Test runner会依次调用setUp、testSecondExample,最后是tearDown方法。假设你有多个測试方法。setUp和tearDown会在一个測试环节调用多次——每经过一个測试用例方法调用一次!

这个故事的寓意是不要放不论什么处理太慢或处理频繁的东西到setUp或tearDown方法里——这会让你执行測试套件时面临漫长的等待

2.创建你的第一个測试

testExample方法仅仅有一个叫做XCTFail的语句,正如它名字里暗示的:总是会失败。这个语句不是很实用,你能够写一个比它更好的!删除testExample方法,并加入例如以下方法:

- (void)test_addition_twoPlusTwo_isFour
{
  XCTAssert(2 + 2 == 4, @"2 + 2 should be 4 but %d was returned instead", 2+2);
}

測试用例的一个经常使用命名标准是:unitOfWork_stateUnderTest_expectedBehavior (工作单元_測试状态_预期行为)。

在这个样例里,被測试的工作单元是加法,測试状态是2 + 2,预期行为是结果为4。

全部XCTest断言都有前缀XCT。XCTAssert是可用于单元測试的简单断言,第一个參数是预评估为ture的表达式,当断言失败时,其后NSLog风格的參数会显示一条消息。

确保项目的当前target为iPhone模拟器,通过窗体顶部文件夹的Product -> Test(Command-U)来执行測试,模拟器会启动并执行測试套件。假设通知处于激活状态,你会看到下列确认消息:

为了证实第一个单元測试成功,切换到Test Navigator。箭头指出了它:

哈哈!

翠绿色的小勾旁边显示出了你的单元測试。

你还能够看到边框空白处菱形图标旁的代码,例如以下所看到的:

这些图标展示关联測试代码的状态:

@implementation旁的绿色小勾表示这个类測试通过。test_addition_twoPlusTwo_isFour旁的绿色小勾表示这种方法測试通过。

同一时候,这些图标也是button:

点击@implementation旁的图标将会执行这个类的全部測试,点击其它測试方法旁的图标则会执行该測试方法,试一试吧!

如今你已经对測试的概念和运行有了初步了解,是时候開始本章的演示样例项目了——測试開始。

二、開始项目

本章的剩余部分你将使用一个名为Reversi的黑白棋游戏项目,规则:两个玩家,分别代表白方和黑方,轮流在8x8棋盘上落子。

通过包围对方棋子来吃掉它。游戏结束时棋子最多的为胜者。

怎样创建这个游戏,请看:
http://www.raywenderlich.com/29228/how-to-develop-an-ipad-board-game-app-part-12

下载本文页尾提供的演示样例项目并执行,点击屏幕下方的Vs Computerbutton与电脑进行对战,感受一下这个游戏的界面和玩法。

你获胜了吗?或者被AI对手爆出翔?无论如何。你的工作不是整日玩游戏——是时候加入一些实用的測试到项目里了。

1.加入測试的支持

第一个须要单元測试的是GameBoard类。

这个类囊括了8x8棋盘的基本逻辑,64个单元格中的每个都有一个状态——空、黑棋或白棋——而且GameBoard实例让你能获取并设置每个方块的状态。

打开GameBoard.h看一下里面的方法,在開始为现有代码编写測试之前,弄清楚各方法的作用和实现是一个好主意。

GameBoard.h。你会看到下列两个方法:

// gets the state of the cell at the given location
// raises an NSRangeException if the column or row are out of bounds
- (BoardCellState) cellStateAtColumn:(NSInteger)column andRow:(NSInteger)row;
// sets the state of the cell at the given location
// raises an NSRangeException if the column or row are out of bounds
- (void) setCellState:(BoardCellState)state forColumn:(NSInteger)column andRow:(NSInteger)row;

cellStateAtColumn:andRow: 和 setCellState:forColumn:andRow: 由你很熟悉的getter/setter模式里发展出来。你的第一个測试是运行例如以下动作:

  • 初始化一个GameBoard实例
  • 设置cell状态
  • 获取cell状态
  • 从指定的cell里获取cell状态

第一步是创建一个GameBoard測试类。右击ReversiGameTests分组。选择 iOS\Cocoa Touch\Objective-C test case class 创建一个名为GameBoardTests的測试类。继承自XCTestCase。

确保你的新測试用例加入到ReversiGameTests target。例如以下图(这个步骤很重要,假设没加入到正确的target里,你的測试不会执行):

打开 GameBoardTests.m 而且删除 testExample 方法,你不须要它。

然后在 GameBoardTests.m 顶部导入头文件(这不过让你的測试类可以訪问GameBoard类):GameBoard.h

#import "GameBoard.h"

你须要为你的全部測试提供一个GameBoard 实例。创建一个实例变量会比在每一个測试里申明一个清爽得多。

GameBoardTests.m 里更新@interface 例如以下:

@interface GameBoardTests : XCTestCase
{
  GameBoard *_board;
}

如今你有了_board实例变量,能够開始測试了。

setUp 方法是第一次初始化_board的好地方,改动setUp例如以下:

- (void)setUp
{
  [super setUp];
  _board = [[GameBoard alloc] init];
}

如今这个类的全部測试用例方法都可以訪问初始化后的_board实例变量了。

2.第一个測试

这是你须要为首个測试用例加入的全部步骤,加入下面方法到GameBoardTests.m:

- (void)test_setAndGetCellState_setValidCell_cellStateChanged
{
  [_board setCellState:BoardCellStateWhitePiece forColumn:4 andRow:5];   BoardCellState retrievedState = [_board cellStateAtColumn:4 andRow:5];
     XCTAssertEqual(BoardCellStateWhitePiece,
           retrievedState,
           @"The cell should be white!");
}

上面的代码在(4,5)单元格里设置了一个白棋,而且立马检索了同样单元格的状态。XCTAssertEqual 断言检查它们是否相等,假设不相等。你会看到一个异常信息,然后你将得知有一些东西须要检查。

上面代码的方法名遵循我之前提到的格式,通过这种方法名,你能够非常easy看出它通过设置正确的单元格位置来測试setter和getter方法,并期待单元格状态的改变。

假设你的測试工作是有计划的,确保iPhone和iPad模拟器都測试,然后执行測试(Command-U)。

切换到Test Navigator,你会看到一个绿色小勾表示測试通过,例如以下图:

这看起来仅仅是一个简单的測试。可是它在调试错误里提供了巨大的价值。

在内部, GAMEBOARD类使用一个简单的二维数组来跟踪8X8棋盘。但假设你以前改变了代表向量或矩阵的数组,本次測试将作为回归測试,确保interface 的基础仍在工作。

作为一个附带的优点,为现有的类编写測试能够大大有助于理解代码是怎样工作的。分析类的方法能够帮助你辨别其功能,并为你编写測试提供便利。

3.測试异常

依照设计的功能測试代码有助于确保其正确性,但也使得你的app“早早失败或高调失败”——那些异常游戏状态或无效条件被调试器非常快抓住。

GameBoard.h里cellStateAtColumn:andRow: 和 setCellState:forColumn:andRow: 方法的凝视表明,假设行或列超出棋盘边框,它们会弹出错误。

看起来你已经找到很多其它的測试条件。

加入下列两个方法:

- (void)test_setCellState_withInvalidCoords_exceptionThrown {
XCTAssertThrowsSpecificNamed(
[_board setCellState:BoardCellStateBlackPiece
forColumn:10
andRow:7], NSException,
NSRangeException,
@"Out-of-bounds board set should raise an exception");
}
- (void)test_getCellState_withInvalidCoords_exceptionThrown {
XCTAssertThrowsSpecificNamed(
[_board cellStateAtColumn:7 andRow:-10],
NSException,
NSRangeException,
@"Out-of-bounds board access should raise an exception");
}

上面的代码里。test_setCellState_withInvalidCoords_exceptionThrown: 试图设置超出范围的单元格(10,7),同一时候test_setCellState_withInvalidCoords_exceptionThrown: 试图获取超出范围的单元格(7,-10)。再次的,方法名已指出在正測试不对的坐标。报出异常正在意料之中。

XCTAssertThrowsSpecificNamed 採用下面四点作为參数:

  • 应该报出异常的表达式
  • 排除的类
  • 排除的名称
  • 測试失败时显示的消息

点击Command-U执行測试。你应该看到下面结果

这是什么?你希望用出色的代码通过測试,可是两个错误标记在Issue Navigator上。測试失败信息也会显示在代码上。例如以下图:

全部的測试失败消息为:

[GameBoardTests test_getCellState_withInvalidCoords_exceptionThrown] failed: (([_board cellStateAtColumn:7 andRow:-10]) throws <NSException, "NSRangeException">) failed: throwing <NSException, "NSGenericException", "row or column out of bounds"> - Out-of- bounds board access should raise an exception

假设你分解上面的消息。你会看到你希望的行为是(throws <NSException, "NSRangeException">) ,而实际发生的是(throwing <NSException, "NSGenericException">) 。

在这个样例里,你期待的是NSRangeException 。但接收到的却是NSGenericException 。

看起来你已经做了一些研究!

演示样例项目地址:

http://pan.baidu.com/s/1o6x6zxg

[iOS翻译]《iOS7 by Tutorials》在Xcode 5里使用单元測试(上)的更多相关文章

  1. iOS单元測试:Specta + Expecta + OCMock + OHHTTPStubs + KIF

    框架选择 參考这篇选型文章,http://zixun.github.io/blog/2015/04/11/iosdan-yuan-ce-shi-xi-lie-dan-yuan-ce-shi-kuang ...

  2. iOS 单元測试之XCTest具体解释(一)

    原创blog,转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS-SDK具体解释专栏 http://blog.csdn.net/column/details/huang ...

  3. ios的单元測试OCUnit以及更新了之后的XCTestCase

    1.像一般创建项目的步骤一样.创建一个用于測试的项目或者打开一个待測试的项目. (oc是5.0之前所使用的測试,如今用的是XCtestCase,默认会创建一个主的測试类.曾经版本号可能非常多步骤省去) ...

  4. ios单元測试之GHUnit

    1.相同创建一个測试的project, 2.通过cocoaPod来下载GHUnit框架,或者到github上下载.由于这个框架是开源的第三方框架. 同一时候加入QuartCore.framework( ...

  5. Appium IOS 自己主动化測试初探

    手机平台的自己主动化測试工具非常多,之前研究过了安卓和苹果的原生自己主动化測试框架.经一些同事介绍,貌似Appium是个不错的工具. 想记录一下研究的结果,也算是篇干货的文章 在网上也看了一些视频.个 ...

  6. iOS自己主动化測试的那些干货

    前言 假设有測试大佬发现内容不正确.欢迎指正,我会及时改动. 大多数的iOS App(没有持续集成)迭代流程是这种 也就是说.測试是公布之前的最后一道关卡.假设bug不能在測试中发现,那么bug 就会 ...

  7. android測试工具MonkeyRunner--google官网翻译

    近期在复习之前的笔记,在回想MonkeyRunner时看了看google官网的内容,写得不错.就翻译出来分享下.事实上google官网真是一个学习的好地方. 基础知识 MonkeyRunner工具提供 ...

  8. 软件測试、ios中的測试概念以及步骤

    软件測试: 软件測试的目标是应该服务于软件项目的目标,能够通过建议反馈使用更加高效的方法和工具,提升软件开发效率以及软件开发质量.同一时候还能够通过过一些手段,更早.更快.很多其它地发现缺陷.从容减少 ...

  9. [iOS翻译]《iOS7 by Tutorials》系列:在Xcode 5里使用单元测试(上)

    简介: 单元测试是软件开发的一个重要方面.毕竟,单元测试可以帮你找到bug和崩溃原因,而程序崩溃是Apple在审查时拒绝app上架的首要原因. 单元测试不是万能的,但Apple把它作为开发工具包的一部 ...

随机推荐

  1. 修改python的pip下载源

    推荐两个源: 豆瓣:http://pypi.douban.com/simple/ 清华:https://pypi.tuna.tsinghua.edu.cn/simple 使用方法有两种,一种为临时使用 ...

  2. 部署hexo后github pages页面未更新或无法打开问题

    title: 部署hexo后github pages页面未更新或无法打开问题 date: 2018-03-30 15:34:29 categories: methods tags: hexo gith ...

  3. python课程设计笔记(五) ----Resuests+BeautifulSoup (爬虫入门)

    官方参考文档(中文版): requests:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html beautifulsou ...

  4. IE兼容opacity

    filter:alpha(opacity=80); opacity: 0.57; /* Firefox, Safari(WebKit), Opera) filter: "alpha(opac ...

  5. Spring中xml文档的schema约束

    最开始使用Spring框架的时候,对于其配置文件xml,只是网上得知其使用方法,而不明其意.最近想着寻根问底的探究一下.以下是本文主要内容: 1.配置文件示例.   <?xml version= ...

  6. C语言提高 (1) 第一天 数据类型本质与内存四区

    (物联网的分层的概念 b/s c/s 结构 习惯: 在C语言 0 函数执行成功 <0是错误 >1做一些返回值处理 3 课前准备 工作经验,记录 4 数据类型的本质 数据类型的本质是固定大小 ...

  7. Codeforces Round #468 (Div. 2, based on Technocup 2018 Final Round)A. Friends Meeting

    Two friends are on the coordinate axis Ox in points with integer coordinates. One of them is in the ...

  8. [Libre 6281] 数列分块入门 5 (分块)

    水一道入门分块qwq 题面:传送门 开方基本暴力.. 如果某一个区间全部都开成1或0就打上标记全部跳过就行了 因为一个数开上个四五六次就是1了所以复杂度能过233~ code: //By Menteu ...

  9. Zend 官方框架增加 Swoole 协程支持 !

    前言 Zend Framework 是 PHP 的官方框架,随着 Zend-Expressive-Swoole 0.2.2 的发布,率先支持了 Swoole 4 的协程功能,现在可以仅通过一个配置即可 ...

  10. @PostConstruct 和 @PreDestroy 指定初始化和销毁方法

    通过实现 @PostConstruct 和 @PreDestroy 注解,也可以指定 bean 的初始化和销毁方法 一.Student 类 public class Student{ public S ...