在iOS中进行测试可以选择XCTest和GHUnit测试两种框架。XCTest集成在Xcode中,而GHUnit则是一个著名的开源框架。
所谓的单元测试,就是指把程序分割成若干个小单元,对每个小单元单独进行测试。所谓的小单元,可以是一个方法,一个UI控件,一次简单的事件等等。本文不重点讨论测试的意义,所以就选择最简单的加法函数,介绍iOS如何进行单元测试。
XCTest的前身是OCUnit,它是一个集成在Xcode中的测试框架。它的使用比较简单,现在Xcode6默认建立的工程就带有XCTest。当然如果是旧的工程没有这个测试,或者不小心删了也没有关系,可以重新添加以下。
首先默认的测试名是“工程名 Tests”的格式。比如我们建立一个项目叫“ios练习”,默认的测试名就是“ios练习 Tests”。
接下来开始从零开始建立自己的单元测试。
首先在项目设置的左下方,选择添加一个target:
然后在other列表内选择Cocoa Touch Testing Bundle。
选择finish即可创建一个新的target。成功添加之后可以看到新加了一个ios__Test.m文件,以及一个新的target。一般情况下系统会自动添加一个scheme,如果没有的话,也可以在左上角运行/停止键的旁边,点击一个scheme,选择新增scheme,并且选择对应的target。
这些配置做好了之后,应该是这样的:
图中标出的三个数字,是应该有的变动。
基本配置做好了之后,就可以开始写代码,使用XCTest了。首先分析一下系统为我们创建的.m的文件:
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
@interface ios___Tests : XCTestCase
@end
@implementation ios___Tests
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
XCTAssert(YES, @"Pass");
}
@end
与写ios不同的是,测试文件没有.h,因为并不需要这么复杂,类的声明在.m文件的@interface里面一起做了。比如这里我们创建了一个ios___Tests类继承自XCTestCase,所有的单元测试类都必须继承自这个基类。系统在执行单元测试的时候,会搜索所有XCTestCase的子类里面,以test开头的方法并执行这些方法。这时候,每一个以test开头的方法就是一个测试用例(TestCase)。比如这里的testExample方法就会当做一个测试用例被执行。
setUp和tearDown方法在每个测试用例的开始和结束时执行。这样可以简化大量代码。比如我需要测试操作数据库的一些方法,那么有必要在setUp方法里面连接数据库并且建表,在tearDown方法里删除表并关闭和数据库的连接。
接下来在ViewController里面添加一个简单的需要被测试的方法并在.h文件里面声明它:
//ViewController.h
- (int)addNumber:(int)a WithNumber:(int)b;
//ViewController.m
- (int)addNumber:(int)a WithNumber:(int)b {
return a + b;
}
然后就可以在测试类里面开始测试这个方法了。
// ios___Tests.m
- (void)testAddMethodRight{
ViewController *vc = [[ViewController alloc] init];
int answer = [vc addNumber:1 WithNumber:2]; // answer = 3
XCTAssertEqual(3, answer);
}
- (void)testAddMethodWrong{
ViewController *vc = [[ViewController alloc] init];
int wrongAnswer = 0;
int answer = [vc addNumber:1 WithNumber:2]; // answer = 3
XCTAssertEqual(wrongAnswer, answer);
}
这里的XCTAssertEqual是一个断言宏,简单来说就是如果它的两个参数值不等,就会报错。这也就是单元测试的目的所在。显然第二个测试无法通过。
接下来运行一下这个target,注意是通过Command + U快捷键运行。然后可以看到如下的运行结果:
Xcode非常贴心的帮我们标出了通过的(绿色的勾)和未通过的测试(红色的叉)。调试区还输出了非常详细的调试信息,包括每个测试失败的原因,测试运行的时间等。
以上就是简单的XCTest测试框架的使用。
GHUnit是Github上一个非常有名的开源框架,但由于需要手动配置,所以相比于内置于Xcode的XCTest,稍微麻烦一些,不过它具有GUI,更加直白,功能也更强大一些。
首先前往https://github.com/gh-unit/gh-unit/downloads下载库文件,解压缩之后可以得到一个名为“GHUnitIOS.framework”的框架。待会儿需要用到这个框架。
接下来再新建一个target,注意target类型是一个普通的“Single Application View”,命名为“GHUnitTest”即可。注意由于Xcode认为这个是一个普通的应用,所以还会为他建立对应的XCTest,由于我们用不着,所以直接删除这个target和对应的文件即可。然后把刚刚下载并解压得到的GHUnitIOS.framework文件添加到GHUnitTest这个target下。配置好的界面如下:
可以看到GHUnitTest的图标和ios练习这个target的图标一致,因为它们都是“Single Application View”。
然后我们需要对这个普通的应用做一些修改,让它不普通起来,变成一个神奇的单元测试应用。首先删除AppDelegate文件,把.h和.m文件都删掉,然后把Supporting Files下的main.m文件改成这样:
#import <UIKit/UIKit.h>
#import <GHUnitIOS/GHUnitIOSAppDelegate.h> //修改引用文件
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([GHUnitIOSAppDelegate class]));// 这里改一下类名
}
}
这里我们不再使用苹果自带的AppDelegate,而是使用GHUnit框架提供的GHUnitIOSAppDelegate。为了避免和主程序冲突,我们把GHUnitTest这个target里面的ViewController改名为GHUViewController。
至此,GHUnit框架的配置就完成了,比XCTest稍微复杂一些。接下来就开始写代码,使用这个框架了。
和XCTest框架一样,只需要.m而把类声明在.m文件里的@interface里即可。新建一个名为“MyGHUnitTestCase.m”的空的OC文件即可。在进行测试之前,需要把被测试的类加到GHUnitTest这个target的Compile Sources里
接下来声明一个名为MyGHUnitTestCase的类,并继承自GHTestCase。完整代码如下:
#import <Foundation/Foundation.h>
#import "ViewController.h"
#import <GHUnitIOS/GHTestCase.h>
@interface MyGHUnitTestCase : GHTestCase{
}
@end
@implementation MyGHUnitTestCase
- (void)setUp{
}
- (void)tearDown{
}
- (void)testAddMethodRight{
ViewController *vc = [[ViewController alloc] init];
int answer = [vc addNumber:1 WithNumber:2]; // answer = 3
GHAssertEquals(answer, 3, @"answer的值应该为3");
}
- (void)testAddMethodWrong{
ViewController *vc = [[ViewController alloc] init];
int wrongAnswer = 0;
int answer = [vc addNumber:1 WithNumber:2]; // answer = 3
GHAssertEquals(answer, wrongAnswer, @"answer的值不应该为0");
}
@end
这个类的测试代码和此前介绍XCTest的时候,几乎一致,所以就不过多介绍了。Command + R运行一下。
GHUnit的GUI做的还是很不错的,有利于开发者调试问题,不过貌似没有适配IOS8…..
点击右上角的Run按钮,每个测试的成败、耗时都显示出来了。点击失败的测试,可以查看详细的方法调用堆栈。
可以看到,XCTest的优势在于快捷方便的配置,而GHUnit的优势在于功能更加强大,因此推荐在进行比较大的项目的时候,考虑GHUnit测试框架。