iOS --- 使用XCTest进行单元测试

原创 2015年12月20日 19:10:19

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

单元测试的使用场景

  1. 对API接口的单元测试,测试驱动开发。
  2. 测试网络接口的可用性。不需要经过多次操作到某个界面再去测试该接口。如分享功能,一般需要特定的逻辑才会触发。若直接测试其API就很方便了。
  3. API性能测试。

setUp和tearDown

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

- (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];
}

因此,常在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已经足够了。

性能测试的宏定义

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

- (void)testPerformanceFilter1 {
    [self measureBlock:^{
        NSInteger filterid = 100;
        UIImage *filteredImage = [FilterTool filterImage:self.image withFilterId:filterid];
        XCTAssertNotNil(filteredImage, @"滤镜效果图应该非空");
    }];
}

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

#define TestFilterPerformance(filterid) - (void)testFilterPerformance_##filterid { \
    [self measureBlock:^{ \
        UIImage *filteredImage = [FilterTool filterImage:self.image withFilterId:filterid]; \
        XCTAssertNotNil(filteredImage, @"滤镜效果图应该非空"); \
    }]; \
}

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

这里写图片描述

异步测试

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

- (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, @"HTTPResponseURL应该与请求的URL一致");
        } else {
            XCTFail(@"返回内容不是NSHTTPURLResponse类型");
        }
    }];
    [task resume];

    // 超时后执行
    [self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
        [task cancel];
    }];
}

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

访问待测类的私有变量

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

#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

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

断言命令

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没有发生具体异常、具体异常名称的异常时通过测试,反之不通过.

Demo

Demo地址: DemoUnitTest

iOS 单元测试之XCTest详解

原创blog,转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS-SDK详解专栏 http://blog.csdn.net/column/details/huan...
  • Hello_Hwc
  • Hello_Hwc
  • 2015年06月28日 13:51
  • 14056

iOS单元测试之接口测试

记得上篇关于单元测试的文章是2015年,当时刚买了芈君的《iOS测试指南》,作为启蒙书籍,将书中的知识点都尝试了一下,但是由于在项目中没有实施,自己对单元测试的重要性和了解并没有太深入。 为什么...
  • HHL110120
  • HHL110120
  • 2017年03月02日 09:49
  • 1291

XCTest的小技巧

XCTest 的小技巧:批量重用测试、测试异步操作
  • pinxue
  • pinxue
  • 2015年07月01日 15:45
  • 1891

'XCTest/XCTest.h' file not found

初学iOS,遇到'XCTest/XCTest.h' file not found问题 从stackoverflow上找到了解决方法,如下: 选择xxxUITests -> Build Se...
  • kevinlinkai
  • kevinlinkai
  • 2016年07月03日 22:06
  • 3614

【iOS】应用XCTest对异步任务进行单元测试

问题提出: XCTest是xcode内置的单元测试框架,方便我们对那些不依赖UI的业务模块做单元测试,但是对于一些网络模块的异步调用,需要将测试模块的生命周期延长,等回调成功才释放。 那么我们如体利用...
  • ruglcc
  • ruglcc
  • 2016年07月11日 09:29
  • 1836

iOS中异步回调的单元测试方法

在XCode6以上版本中,苹果添加了用于异步回调测试的api,因此不用像旧版本那样,发起异步调用后通过循环查询标志位,来检查异步回调函数的调用了。 在新版本中直接使用XCTestExpectatio...
  • ucxiii
  • ucxiii
  • 2016年08月11日 14:33
  • 2832

XCode中的单元测试:编写测试类和方法(内容意译自苹果官方文档)

当你在工程中通过测试导航栏添加了一个测试target之后, xcode会在测试导航栏中显示该target所属的测试类和方法。 这一章演示了怎么创建测试类,以及如何编写测试方法。测试targets,...
  • openglnewbee
  • openglnewbee
  • 2016年06月25日 16:54
  • 1835

iOS开发之单元测试

iOS开发:XCTest单元测试(附上一个单例的测试代码) 测试驱动开发并不是一个很新鲜的概念了。在我最开始学习程序编写时,最喜欢干的事情就是编写一段代码,然后运行观察结果是否正...
  • u011597585
  • u011597585
  • 2015年12月24日 17:07
  • 538

iOS开发:XCTest单元测试(附上一个单例的测试代码)

测试驱动开发并不是一个很新鲜的概念了。在我最开始学习程序编写时,最喜欢干的事情就是编写一段代码,然后运行观察结果是否正确。我所学习第一门语言是c语言,用的最多的是在算法设计上,那时候最常做的事情就是编...
  • yishengzhiai005
  • yishengzhiai005
  • 2016年12月27日 14:18
  • 855

iOS测试之移动端测试用例设计总结

一、安装/卸载1、真机上安装、卸载、高版本覆盖安装、低版本覆盖安装、卸载后安装高版本; 安装关注点:版本号、渠道号、数字签名(用抓包工具辅助查看)、安装成功后启动向导、安装过程中对意外情况的处理(取消...
  • yxys01
  • yxys01
  • 2017年08月29日 17:55
  • 921
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:iOS --- 使用XCTest进行单元测试
举报原因:
原因补充:

(最多只允许输入30个字)