XCTest进行单元测试

测试驱动开发(TDD)是当前流行的开发理念,XCTest是Apple封装的单元测试类库。使用XCTest进行单元测试的流程比较简单,本博客仅简单介绍下XCTest的使用。但对于单元测试的理解(何时使用,如何更高效地驱动开发)却是一个需要积累的过程,要在TDD的过程中仔细体会。

setUp和tearDown

一个测试用例以一个Objective-C类的形式存在,其implementation中有默认的setUp和tearDown方法,分别用于处理用例执行前的准备工作和执行完毕后的清理工作。

?
1
2
3
4
5
6
7
8
9
<code class = "hljs coffeescript" >- ( 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];
}</code>

因此,常在setUp中进行基本对象的初始化等一些必需的步骤。

单元测试

所有以test开头的实例方法(不能有参数)都是一个完整的单元测试的case。

这里写图片描述

最左边有个小图标,点击之后即可执行该测试case。

性能测试

性能测试可直接使用 - (void)measureBlock:(void (^)(void))block; 进行,传递参数即为要执行任务组成的一个block。
如以下的case可用于测试measureBlock接收的block参数中执行的性能。

这里写图片描述

Baseline即可设置该性能是否合格的参考基准。
注意,一个性能测试case中只能执行一次measureBlock,即只能测试一个指定block步骤的性能(这其实也是合乎情理的,混到一块了哪里还能区分出性能测试的结果)。因此,在while,for等循环中是不能使用measureBlock的,否则会出错。
measureMetrics的用法跟measureBlock类似。
当然,也可以使用startMeasuring和stopMeasuring方法进行性能测试,但绝大多数情况下使用measureBlock已经足够了。

性能测试的宏定义

例如,在单独测试一个图片滤镜的性能时,可以这样:

?
1
2
3
4
5
6
7
<code class = "hljs objectivec" >- ( void )testPerformanceFilter1 {
     [self measureBlock:^{
         NSInteger filterid = 100 ;
         UIImage *filteredImage = [FilterTool filterImage:self.image withFilterId:filterid];
         XCTAssertNotNil(filteredImage, @ "滤镜效果图应该非空" );
     }];
}</code>

那么,如果有几十个滤镜需要进行性能测试呢?
因为单元测试case的方法名不能传递参数,且循环中不能使用measureBlock,因此对于大量重复的性能测试case,目前看来采用宏定义是比较好的解决方法。

?
1
2
3
4
5
6
<code class = "hljs smalltalk" >#define TestFilterPerformance(filterid) - ( void )testFilterPerformance_##filterid { \
     [self measureBlock:^{ \
         UIImage *filteredImage = [FilterTool filterImage:self.image withFilterId:filterid]; \
         XCTAssertNotNil(filteredImage, @ "滤镜效果图应该非空" ); \
     }]; \
}</code>

则使用起来,就可如下定义所有的测试case了。

这里写图片描述

异步测试

XCTest提供了XCTestExpectation来进行异步测试,即可在指定时机使用其fulfill实例方法开始执行断言命令。例如以下是一个完整的网络请求的测试样例,可用于测试server端是否正常,要在对应的block中进行断言。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<code class = "hljs haskell" >- ( void )testAsynchronousURLConnection {
     XCTestExpectation *expectation = [self expectationWithDescription:@ "GET Baidu" ];
 
     NSURL *url = [NSURL URLWithString:@ "http://www.baidu.com/" ];
     NSURLSession *session = [NSURLSession sharedSession];
     NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
         // XCTestExpectation条件已满足,接下来的测试可以执行了。
         [expectation fulfill];
         XCTAssertNotNil(data, @ "返回数据不应非nil" );
         XCTAssertNil(error, @ "error应该为nil" );
         if (nil != response) {
             NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
             XCTAssertEqual(httpResponse.statusCode, 200 , @ "HTTPResponse的状态码应该是200" );
             XCTAssertEqual(httpResponse.URL.absoluteString, url.absoluteString, @ "HTTPResponse的URL应该与请求的URL一致" );
         } else {
             XCTFail(@ "返回内容不是NSHTTPURLResponse类型" );
         }
     }];
     [task resume];
 
     // 超时后执行
     [self waitForExpectationsWithTimeout: 10 handler:^(NSError * _Nullable error) {
         [task cancel];
     }];
}</code>

即:异步网络请求操作正常执行,但断言命令仅在[expectation fulfill];结束后才开始执行,这样能确保得到网络请求的返回结果后执行相应断言。同时,可设置超时时间及对应操作。
否则,就需要使用定时机制等处理类似情况。

访问待测类的私有变量

在case中,如何访问一个待测类的私有变量呢?对该类进行相应的扩展即可。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<code class = "hljs objectivec" ># import <xctest xctest.h= "" >
# import "FilterTool.h"
 
@interface FilterTool (UnitTest)
 
@property (strong, nonatomic) FilterController *filterController;
 
- ( void )didSelectFilterCellAtIndexPath:(NSIndexPath *)indexPath;
 
@end
 
 
@interface FilterToolTest : XCTestCase
 
@end
 
@implementation FilterToolTest
 
XXXX
 
@end </xctest></code>

FilterTool类的filterController属性和didSelectFilterCellAtIndexPath:方法本是私有的,若想对其进行测试,则必须将其加入扩展。

断言命令

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<code class = "hljs mel" >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没有发生具体异常、具体异常名称的异常时通过测试,反之不通过.</code>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值