您的位置:首页 > 移动开发 > IOS开发

IOS开发笔记之三——XCTest单元测试框架的使用

2014-07-25 13:24 501 查看
第1章 前言

单元测试是一种白盒测试,白盒测试是一种细粒度的测试,具体到方法、函数和异常测试。顾名思义,单元测试也就是编码。在我们的软件的开发中,有时自己写了一些逻辑性较强的代码,没有人能够保证自己的代码第一次写出来就能达到要求。所以,需要去验证测试自己的这段代码,实际开发中,由于大家在项目工程中开发,想要程序走到自己的代码,需要很多繁琐的无关的步骤,这样如果要是需要做大量的测试时,针对性不强,就会浪费很多的无谓的时间。

当然,你可以另写一个demo,在demo中测试自己的代码逻辑。本次预研,就是为了可以在工程项目中直接通过调用你的方法来测试,要方便快捷很多。

本次预研历时约一周,由于项目开发等工作,所以,投入的时间间间断断,并没有投入足够集中的时间,单元测试是一个很大的领域,这篇文档只是带领入门,掌握简单的框架使用,可以方便测试你的逻辑代码。

此次主要介绍Xcode 5.0 自带的XCTest测试框架,属于部门开发任务,投入时间有限。后面若有时间,希望可以继续深入研究。

第2章 使用XCTest测试框架

在Xcode5.0以前的版本,Xcode自带的测试框架是OCUnit,这个已经被废止,但是经过验证,它还是可以继续使用的,在这里就暂不介绍了。

(1)添加XCTest到StockMaster工程中

如下图所示,通过创建target方式添加方式,需要File—>New—>Target—>IOS—>Other,选择Cocoa Touch Unit Testing Bundle模板。



选择自己要测试的Target,Type选择XCTest。点击Finish,之后你就可以在工程里面看到添加的测试框架目录,里面有默认生成的测试类。文件的目录如下图所示:



默认测试类中,有个自动生成的测试用例,这个测试用例默认是执行失败的。



(2) XCTest框架的设置

我们来尝试执行这个测试用例,再写一个默认执行通过的测试用例。如下:



然后,点击Edit Scheme,首先选择Destination目标设备(注意:逻辑单元测试只能在模拟器中测试,不能选择IOS Device)。



Run Enterprise**—>选择Debug



选择Test,Build Configuration选择Debug,并确保将测试用例勾选。



(3) 运行XCTest测试用例

Command + U运行测试或者选择Product—>Test,运行结果如下:



从图上可以看出,测试报告内容,包括测试用例执行时间、执行通过与否。当然要想仔细显示结果,需要我们写出详尽的测试用例。

第3章 怎样测试我们工程中的方法

当我们在自己写了一段逻辑比较复杂的逻辑时,而这段程序需要足够的测试,那么XCTest就可以发挥出它重要的作用了。

(1) 某个功能代码中,有个TestController类,类中有个字符串处理的方法

(NSMutableArray *) formatStringtoArray:(NSString *) wString segmentLetter:(NSString *) letter

它的作用是把NSString型的字符串,用letter字符分割开,分割的结果,存到NSMutableArray数组中。例如:

NSString 为“what,do,you,think,of,china,football”

处理后:

tempArray结果为:

what,

do,

you,

think,

of,

china,

football

共7个元素。

(2) 初始化工作

setup方法是初始化方法,tearDown方法是释放资源的方法,它们在每次调用测试方法之前和之后调用。初始化代码如下:

@interface Enterprise**_Tests : XCTestCase

@property(nonatomic, strong) TestController *testChatController;

@end

@implementation Enterprise**_Tests

- (void)setUp

{

[super setUp];

self.testController= [[TestController alloc] initWithNibName:@"TestController" bundle:nil];

}

- (void)tearDown

{

self.testController= nil;

[super tearDown];

}

(3) 书写测试用例,测试的质量如何直接取决于你的测试用例的质量。另外,测试用例的命名须要以test开头进行命名。对于这个方法我写了以下10个测试用例:

- (void)testFormatStringtoArray1

{

NSString *testString = @"what,do,you,think,of,china,football";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSLog(@"The formatted results:
%@",testArray);

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 7, @"期望值是:7,实际值是:%lu", (long)countArray);

}

- (void)testFormatStringtoArray2

{

NSString *testString = @"what,";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSLog(@"The formatted results:
%@",testArray);

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 1, @"期望值是:1,实际值是:%lu", (long)countArray);

}

- (void)testFormatStringtoArray3

{

NSString *testString = @",do";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSLog(@"The formatted results:
%@",testArray);

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 1, @"期望值是:1,实际值是:%lu", (long)countArray);

}

- (void)testFormatStringtoArray4

{

NSString *testString = @",";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSLog(@"The formatted results:
%@",testArray);

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 0, @"期望值是:0,实际值是:%lu", (long)countArray);

}

- (void)testFormatStringtoArray5

{

NSString *testString = @",,,";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 0, @"期望值是:0,实际值是:%lu", (long)countArray);

}

- (void)testFormatStringtoArray6

{

NSString *testString = @"what,,do,,,you,,,think..,of,;;china;;..,..football?";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 7, @"期望值是:7,实际值是:%lu", (long)countArray);

}

- (void)testFormatStringtoArray7

{

NSString *testString = @"whatdoyouthinkofchinafootball?";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSLog(@"The formatted results:
%@",testArray);

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 1, @"期望值是:1,实际值是:%lu", (long)countArray);

}

- (void)testFormatStringtoArray8

{

NSString *testString = @"what.do,you,think, ,of, ,china, ,football?";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSLog(@"The formatted results:
%@",testArray);

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 6, @"期望值是:6,实际值是:%lu", (long)countArray);

}

- (void)testFormatStringtoArray9

{

NSString *testString = @"what|,do,|you|,think, ,of, ,china, ,football?";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSLog(@"The formatted results:
%@",testArray);

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 7, @"期望值是:7,实际值是:%lu", (long)countArray);

}

- (void)testFormatStringtoArray10

{

NSString *testString = @"what,,do,,you,,think,,, ,of,,,,,,,,,,,,,,,,,, ,china, ,football?";

NSString *testSeg = @",";

NSMutableArray *testArray = [[NSMutableArray alloc] init];

testArray = [_testController formatStringtoArray:testString segmentLetter:testSeg];

NSLog(@"The formatted results:
%@",testArray);

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 7, @"期望值是:7,实际值是:%lu", (long)countArray);

}

经过command + U运行编译,输出的测试报告如下:



10个测试用例,只通过了5个,可见我当初的那段代码写的是多么有问题以及单元测试的必要性。

第4章 XCTest测试框架基本原理介绍

(1)测试运行时序图

前面已经说过,测试类中会调用setup和tearDown方法,setUp是初始化方法,tearDown是释放资源的方法,他们在每次调用测试方法之前和之后调用。

因此,在测试类运行的生命周期中,这两个方法可能会多次运行,它们的时序图如图所示:



(2)框架中的断言测试(参考相关文档)

下面一共18个断言(SDK中也是18个,部分宏已经测试过):

XCTFail(format…) 生成一个失败的测试;

XCTAssertNil(a1, format...)为空判断,a1为空时通过,反之不通过;

XCTAssertNotNil(a1, format…)不为空判断,a1不为空时通过,反之不通过;

XCTAssert(expression, format...)当expression求值为TRUE时通过;

XCTAssertTrue(expression, format...)当expression求值为TRUE时通过;

XCTAssertFalse(expression, format...)当expression求值为False时通过;

XCTAssertEqualObjects(a1, a2, format...)判断相等,[a1 isEqual:a2]值为TRUE时通过,其中一个不为空时,不通过;

XCTAssertNotEqualObjects(a1, a2, format...)判断不等,[a1 isEqual:a2]值为False时通过;

XCTAssertEqual(a1, a2, format...)判断相等(当a1和a2是 C语言标量、结构体或联合体时使用,实际测试发现NSString也可以);

XCTAssertNotEqual(a1, a2, format...)判断不等(当a1和a2是 C语言标量、结构体或联合体时使用);

XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)判断相等,(double或float类型)提供一个误差范围,当在误差范围(+/-accuracy)以内相等时通过测试;

XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...) 判断不等,(double或float类型)提供一个误差范围,当在误差范围以内不等时通过测试;

XCTAssertThrows(expression, format...)异常测试,当expression发生异常时通过;反之不通过;

XCTAssertThrowsSpecific(expression, specificException, format...) 异常测试,当expression发生specificException异常时通过;反之发生其他异常或不发生异常均不通过;

XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression发生具体异常、具体异常名称的异常时通过测试,反之不通过;

XCTAssertNoThrow(expression, format…)异常测试,当expression没有发生异常时通过测试;

XCTAssertNoThrowSpecific(expression, specificException, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过;

XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过

XCTAssertEqualObjects(a1, a2, format...)的判断条件是[a1 isEqual:a2]是否返回一个YES。

XCTAssertEqual(a1, a2, format...)的判断条件是a1 == a2是否返回一个YES。

第5章 你可能遇到的问题以及解决方案

1、 找不到相关文件问题

(1)问题描述



(2)问题分析与参考解决方案

当你在自己创建的target类中调用测试类时,被测试类有可能会链接其他文件库,而这些库并没有被链接到target中,此时需要手动添加缺少文件的所在的文件目录,

由于我们的工程链接库比较复杂,我也不确定文件链接的关系,目前只有这样机械的手工添加。

找到缺少文件目录,例如:

/Users/ztli/Desktop/***/***/MTIA/MTIA/

操作如下:



注意:

(1)此时被测试类的TestController 类右侧的Target Membership中的Enterprise** Tests不要轻易选上。

(2)如果上述方法仍然不能解决此问题,可以直接将缺少的文件找到,放置到测试类文件的Supporting Files文件夹下。

2、测试Configuration选择问题

(1) 问题描述:

在创建好测试target时,会出现类似如下的错误提示:

Undefined symbols for architecture i386:

"_OBJC_CLASS_$_TestController", referenced from:

objc-class-ref in Enterprise**_Tests.o

ld: symbol(s) not found for architecture i386

clang: error: linker command failed with exit code 1 (use -v to see invocation)

(2) 问题分析及参考解决方案:

造成这个问题一般与你的Scheme配置造成,进入Scheme编辑页面,保证如下图的选择配置:

Build选项,对应target勾选(至少选择Test)



Test选项,Build configuration选择Debug,而不是release。



3、格式字符串问题

(1) 问题描述:

XCTAssertTrue ([testArray count] == 7, @"期望值是:7,实际值是:%d",[testArray count]);



使用XCTAssertTrue时,会有以上的格式警示,如图所示。

(2) 问题分析与参考解决方案:

The cause of the warning in this case comes from the use of a %d in the format specifier which expects a signed 32-bit integer. However the value supplied was an NSInteger which will be a 64-bit integer with the 64-bit runtime. The fix in this case is to
explicitly specify a long integer in the format string ( %ld ) and cast the NSInteger to a long:

NSUInteger countArray = [testArray count];

XCTAssertTrue([testArray count] == 7, @"期望值是:7,实际值是:%lu", (long)countArray);

(3)参考链接:

http://www.tuicool.com/articles/vumyEra

第6章 小结

这本只是针对Xcode 5.0自带框架的XCTest的介绍,IOS的单元测试还有很多功能强大的第三方开源测试框架,

例如GHUnit、OCMock等,它们拥有自己特长。值得去深入研究,本文暂不进行介绍,望以后还可以继续研究。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐