xCode6 单元测试(Uint Test)

xCode6 单元测试(Uint Test)

单元测试的意义(Motion)

  • 找到bug

  • 保证代码质量

  • 持续集成

  • 代码覆盖

测试框架(XCTest)

xcode内置了XCTest框架支持。

如何写一个测试案例(Test Case)

  • 通过实现一个XCTestCase子类。
1
2
@interface  MyTestClass : XCTestCase {
}
  • 实现相关Test方法。
    -(void)testMyFunctionWorks;
    类的test开头的方法为测试方法。
  • 使用断言API报告代码错误。
    XCTAssertEqual(value, expectedValue);
    XCUnit内置了很多断言,可到头文件中查看。
  • setup, teardown方法。
    可以在setup中提供测试的前置条件和获取资源,在teardown中重置前置条件和释放资源。在每个测试案例执行前调用setup方法,直行后调用teardown方法。所以特别留意setup和teardown的执行性能

单元测试所需资源

  • 测试代码
  • 用到的资源文件
    xcode创建项目时可以同时创建测试项目。测试项目也很容易添加到现有项目当中。

单元测试的执行环境

单元测试是注入到主项目代码中执行的。
但是测试项目的资源文件不会包含进主项目的资源文件。所以测试项目代码中不能使用[NSBundle mainBundle]方法获取资源。

执行单元测试

  • 用快捷键CMD+U执行单元测试。
  • 使用菜单”Product”->”Test”执行单元测试。
  • 使用导航栏的按钮执行单个测试案例。
  • 使用xcodebuild命令行工具执行。

xcodebuild test
-project ~/Documents/MyApp.xcodeproj
-scheme MyApp
-destination ‘platform=iOS,name=iPhone’

查看测试结果

  • 在Test Navigator 查看。
  • 在Issue Navigator查看。
  • 在Source Editor side bar中查看。
  • 在Test Reports中查看。

OCUnit 升级

从xcode5.1开始,OCUnit框架被抛弃了。可以通过Edit > Refactor > Convert to XCTest把旧框架移植到XCUnit。

异步测试(Asynchronous Testing)

越来越多的APIs是异步执行的。

  • Block调用
  • 委托回调(Delegate callbacks)
  • 网络请求
  • 后台处理(Background processing)
    但是单元测试都是同步执行的。现在XCUnit提供了异步函数的测试支持。

新增的支持异步测试的APIs:

使用XCTestExpectation对象描述一个期望的事件。

1
- (XCTestExpectation *)expectationWithDescription:(NSString *)description;

然后XCUnit框架会等待期望的事件的值被填充。

1
2
- (void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout
handler:(XCWaitCompletionHandler)handlerOrNil;

一个测试例子

1
2
3
4
5
6
7
8
9
10
- (void)testDocumentOpening
{
XCTestExpectation *expectation = [self expectationWithDescription:@“open doc”];
UIDocument *doc = ...;
[doc openWithCompletionHandler:^(BOOL success) {
XCTAssert(success);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}

性能测试

动机
  • 代码的改变会影响性能的变化。

  • 很难捕捉到这种变化。

  • 单元测试可以自动监控这些变化。

    性能测试的支持
  • 增加了测量性能的APIs。

  • 使用新的UI来解释结果。

  • 使用Instuments来分析(Profiling )性能。

测量性能的新APIs
  • measureBlock API

    1
    - (void)measureBlock:(void (^)(void))block;

measureBlock执行10次block代码,测量事件,然后把结果显示在xcode中。

一个例子

1
2
3
4
5
6
7
8
9
- (void)testUseFileHandlePerformance
{
[self measureBlock:^{
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:PATH];
XCTAssertNotNil(fileHandle);
UseFileHandle(fileHandle);
[fileHandle closeFile];
}];
}
  • measureMetrics API
1
2
- (void)measureMetrics:(NSArray *)metrics automaticallyStartMeasuring:
(BOOL)automaticallyStartMeasuring withBlock:(void (^)(void))block;
  • startMeasuring API
1
- (void)startMeasuring;
  • stopMeasuring API
1
- (void)stopMeasuring;

综合例子

1
2
3
4
5
6
7
8
9
10
11
12
- (void)testUseFileHandlePerformance
{
[self measureMetrics:@[XCTPerformanceMetric_WallClockTime]
automaticallyStartMeasuring:NO forBlock:^{
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:PATH];
XCTAssertNotNil(fileHandle);
[self startMeasuring];
UseFileHandle(fileHandle);
[self stopMeasuring];
[fileHandle closeFile];
}];
}

important: 性能测试的关键是精确测量和建立测试基线.
基线的两种常见形式: 平均值基线和方差基线。

其他

  • 使用测试项目资源
    +[NSBundle bundleForClass:[MyTest class]]