简单介绍:

单元測试是软件开发的一个重要方面。毕竟,单元測试能够帮你找到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. 杂项:DS(目录服务)

    ylbtech-杂项:DS(目录服务) 1.返回顶部 1. DS(目录服务). 目录服务管理概述: 目录服务是扩展计算机系统中最重要的组件之一.虽然用户和管理通常不知道他们感兴趣对象的确切名称,但他们 ...

  2. Hdu-6230 2017CCPC-哈尔滨站 A.Palindrome Manacher 主席树

    题面 题意:给你一个字符串,问你满足s[i]=s[2n-i]=s[2n+i-2]的子串(这子串长度为3n-2)有多少个,原字符串长度<=5e5 题解:对于这种子串,其实要满足2个回文,跑过一次M ...

  3. 不用任何插件,实现一个tab栏切换

    //使用jquery中获取当前索引的方法.显示隐藏 <script> $(".tab_list li").on('click', function () { $(thi ...

  4. 百度地图api的简单应用

    百度地图api 获取经纬度(通过浏览器的) //获取经纬度 window.navigator.geolocation.getCurrentPosition(function(position) { a ...

  5. 1) 十分钟学会android--建立第一个APP,创建android项目

    一个Android项目包含了所有构成Android应用的源代码文件. 本小节介绍如何使用Android Studio或者是SDK Tools中的命令行来创建一个新的项目. Note:在此之前,我们应该 ...

  6. Java 解决一些ACM中大数问题

    大数中算术运算结果的首选标度 运算 结果的首选标度 加 max(addend.scale(), augend.scale()) 减 max(minuend.scale(), subtrahend.sc ...

  7. ES: 机器学习、专家系统、控制系统的数学映射

    一.基本定义    1.机器学习维基定义:机器学习有下面几种定义: "机器学习是一门人工智能的科学,该领域的主要研究对象是人工智能,特别是如何在经验学习中改善具体算法的性能". & ...

  8. spring helloword

    控制反转: Inversion on Control , 控制反转 IOC 对象的创建交给外部容器完成,这个就做控制反转. 依赖注入,  dependency injection 处理对象的依赖关系 ...

  9. 用shell编写一个三角形图案

    第一种方法 #!/bin/bash read -p "请输入层数: " n for (( i=1; i<=$n;i++ ))do   for (( j=n; j>=i; ...

  10. BZOJ 3456: 城市规划 多项式求逆

    Description 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.  刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接 ...