iOS单元测试简介和利用

分享
手机游戏开发者 2024-9-23 15:28:34 49 0 来自 中国
一、单元测试简介

1.1、简介

单元测试(Unit Testing),又称为模块测试,是指对软件中的最小可测试单元进行查抄和验证,通过开发者编写代码去验证被测代码是否精确的一种本事,比方编写一个测试函数去测试某一功能函数是否能精确实行到达预期效果。在现实项目开发中利用单元测试可以进步软件的质量,也可以只管早的发现代码中存在的标题加以修正。
实行单元测试,是为了证明某段代码的行为确实和开发者所盼望的同等。因此,我们所要测试的是规模很小的、非常独立的功能片断。通过对全部单独部门的行为创建起信心。然后,才气开始测试整个体系。
1.2、单测应用

一连集成(Continuous Integration),简称CI,是软件开发周期的一种实践,把代码堆栈(Gitlab大概Github)、构建工具(如Jenkins)和测试工具(SonarQube)集成在一起,频仍的将代码归并到主干然后主动进行构建和测试。简单来说一连集效果是一个监控版本控制体系中代码厘革的工具,当发生厘革是可以主动编译和测试以及实行后续自界说动作。
1.png 二、单元测试利用

点击下载Demo:ZJHUnitTestDemo
2.1、创建测试项目

在创建项目时勾选 Include Tests选项,如下图所示:
2.png 创建项目乐成后,项目目次下即可看到对应的单元测试文件夹。先忽略ZJHUnitTestDemoUITests,它属于UI测试,其他文章会有更多先容,本文重要讲ZJHUnitTestDemoTests文件
3.png 2.1.2、项目创建后添加

如果之前的项目还没有添加单元测试target,也可以按照下图方式进行新建:
2.2、单元测试类先容

在新建的测试文件代码如下所示,体系主动天生了几个方法:
#import <XCTest/XCTest.h>// 全部的测试类必要继承 XCTestCase@interface ZJHUnitTestDemoTests : XCTestCase@end@implementation ZJHUnitTestDemoTests/// 在每一个测试方法调用前,都会被调用;用来初始化 test 用例的一些初始值- (void)setUp {    // Put setup code here. This method is called before the invocation of each test method in the class.    // 在这里设置代码。在调用类中的每个测试方法之前调用此方法。}/// 在每一个测试方法调用后,都会被调用;用来重置 test 方法的数值- (void)tearDown {    // Put teardown code here. This method is called after the invocation of each test method in the class.    // 在这里输入删除代码。在调用类中的每个测试方法之后调用此方法。}/// 测试方法定名以 test 开始- (void)testExample {    // This is an example of a functional test case.    // Use XCTAssert and related functions to verify your tests produce the correct results.    // 这是一个功能测试用例。    // 利用XCTAssert和相干函数来验证您的测试产生精确的效果。}/// 性能测试- (void)testPerformanceExample {    // This is an example of a performance test case.    // 这是一个性能测试用例。    [self measureBlock:^{        // Put the code you want to measure the time of here.        // 把你想要丈量时间的代码放在这里。    }];}@endsetUp方法:setUp方法会在XCTestCase的测试方法每次调用之前调用,以是可以把一些测试代码必要用的初始化代码和全局变量写在这个方法里;
tearDown:在每个单元测试方法实行完毕后,XCTest会实行tearDown方法,以是可以把必要测试完成后烧毁的内容写在这个里,以便包管下面的测试不受本次测试影响
测试用例:全部测试的方法都必要以test为前缀进行定名,比如- (void)testExample,- (void)testPerformanceExample
2.3、新建示例

我们在项目里面创建一个ZJHMathTool类:
@interface ZJHMathTool : NSObject- (int)sumAint)a andBint)b;- (int)subAint)a andBint)b;- (int)multiplyAint)a andBint)b;- (int)divideAint)a andBint)b;@end@implementation ZJHMathTool- (int)sumAint)a andBint)b {    return a + b;}- (int)subA:(int)a andB:(int)b {    return a - b;}- (int)multiplyA:(int)a andB:(int)b {    return a * b;}- (int)divideA:(int)a andB:(int)b {    return a / b;}@end同时在新建一个对应的 ZJHMathToolTests测试类:
#import <XCTest/XCTest.h>@interface ZJHMathToolTests : XCTestCase@end@implementation ZJHMathToolTests- (void)setUp {}- (void)tearDown {}- (void)testExample {}- (void)testPerformanceExample {    [self measureBlock:^{    }];}@end2.4、逻辑测试

接下来我们开始编写用例,来测试ZJHMathTool中的方法,如下所示
@interface ZJHMathToolTests : XCTestCase@property (nonatomic, strong) ZJHMathTool *mathTool;@end@implementation ZJHMathToolTests// 新建ZJHMathTool对象- (void)setUp {    self.mathTool = [ZJHMathTool new];}// 烧毁ZJHMathTool对象- (void)tearDown {    self.mathTool = nil;}// 测试加法- (void)testMathAdd {    int result = [self.mathTool sumA:2 andB:3];    XCTAssert(result == 5, @"加法盘算堕落");}// 测试减法- (void)testMathSub {    int result = [self.mathTool subA:5 andB:2];    XCTAssert(result == 3, @"减法盘算堕落");}@end运行测试用例 :
代码编辑器边栏菱形按钮,测试单个用例
Test 导航栏,测试单个用例
快捷键 command + U测试全部用例
利用下令行工具 xcodebuild 可以测试单个用例,也可以测试全部用例
2.5、性能测试

性能测试通过分量代码块实行所斲丧的时间是非,来衡量是否通过测试。
2.5.1、测试方法预备

新建 ZJHPerson 类,然后添加一个循环打印方法。
@interface ZJHPerson : NSObject- (void)sayHello;@end@implementation ZJHPerson- (void)sayHello {    for (int i = 0; i < 1000; i++) {        NSLog(@"hello");    }}@end然后再新建 ZJHPerson 对象测试类 ZJHPersonTests。
#import <XCTest/XCTest.h>#import "ZJHPerson.h"@interface ZJHPersonTests : XCTestCase@property (nonatomic, strong) ZJHPerson *person;@end@implementation ZJHPersonTests- (void)setUp {    self.person = [ZJHPerson new];}- (void)tearDown {    self.person = nil;}- (void)testPerformanceExample {    [self measureBlock:^{        [self.person sayHello];    }];}@end2.5.2、性能测试API

有两个API可以利用
- measureBlock重要是通过block内部代码块的实行时间来测试性能,通过设置baseline(基准)和stddev(尺度毛病)来判断方法是否能通过性能测试。
- (void)testPerformanceOfMyFunction {        [self measureBlock:^{            //做你想丈量的东西。            MyFunction();        }];  }- measureMetrics:automaticallyStartMeasuring:forBlock丈量代码块的性能,可以选择推迟丈量的起点。
- startMeasuring在代码块中开始性能度量。
- stopMeasuring结束代码块内的性能度量。
defaultPerformanceMetrics标识在调用measureBlock:时度量的性能指标。
XCTPerformanceMetricXCTest可以丈量的性能指标。
- (void)testMyFunction2_WallClockTime {        [self measureMetrics:[self class].defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{            // 做设置工作,必要为每个迭代,但你不盼望在调用- startmeasurement之进步行丈量            SetupSomething();            [self startMeasuring];            // 做你想丈量的东西。            MyFunction();            [self stopMeasuring];            //实行每次迭代都必要实行的分解工作,但你不想在调用- stopmeasurement后进行度量            TeardownSomething();        }];    }2.5.3、设置基准线

全部的性能测试必要设置一个Baseline来验证是否通过测试,没有设置的会提示No baseline average for Time。点击左边灰色菱形图标可检察性能测试效果。
在性能测试效果图里可以看到匀称时间(总时长/10),尚有10个柱状图,这个意思是在这个测试方法运行总时长被分为10份,蓝色柱子表现每份的耗时,中央的横线表现匀称时间,点击数字可检察每份中的匀称时长。

  • Metric:度量单元,Time为时间
  • Result:度量效果
  • Average:度量时间匀称值
  • Baseline:度量的基准线
  • Max STDDEV:最大容错率
点击Edit可以进行编辑,我设置的基定时间是0.15s,最大容错率是10%,运行效果是0.147s,好于根本先2%,全部可以通过。设置时基定时间为0.05s时就会堕落。
2.6、异步测试

什么时间必要利用异步测试:

  • 打开文档
  • 在背景线程中实行的服务和网络运动
  • 执办法画
  • UI 测试时
2.6.1 异步测试XCTestExpectation

异步测试分为3个部门: 新建盼望等待盼望被推行推行盼望

  • XCTestExpectation:测试盼望,可以由测试类持有,也可以自己持有,自己持有测试盼望时机动性更好一些,你可以选择等待哪些盼望。
  • waitForExpectations:timeout: :等待异步的盼望代码实行,根据初始化方式差异,等待的方法差异。
  • fulfill :推行盼望,而且得当参加XCTAssertTrue等断言,来验证测试效果。
/// 异步测试XCTestExpectation:测试类持有盼望- (void)testAsyncMethod1 {    // 新建盼望:测试类持有的初始化方法    XCTestExpectation *expect1 = [self expectationWithDescription"asyncTest1"];    // 推行盼望:实行异步操纵    [ZJHNetworkTool requestUrl"getTestData" param{} completion:^(NSDictionary * _Nonnull respondDic) {        // 异步结束,标注盼望告竣        [expect1 fulfill];    }];        // 等待盼望被推行:测试类持偶然的等待方法    [self waitForExpectationsWithTimeout:3.0 handler:^(NSError * _Nullable error) {        NSLog(@"***ZJH error : %@", error);    }];}/// 异步测试XCTestExpectation:自己类持有盼望- (void)testAsyncMethod2 {    // 新建盼望:自己持有的初始化方法    XCTestExpectation *expect2 = [[XCTestExpectation alloc] initWithDescription"asyncTest2"];            // 推行盼望:实行异步操纵    [ZJHNetworkTool requestUrl"getTestData" param{} completion:^(NSDictionary * _Nonnull respondDic) {        XCTAssertTrue([respondDic[@"code"] isEqualToString"200"]);        // 异步结束,标注盼望告竣        [expect2 fulfill];    }];    // 等待盼望被推行:自己持偶然的等待方法    [self waitForExpectations[expect2] timeout:3];}2.6.2 异步测试XCTWaiter

XCTWaiter是 2017 年新增的异步测试方案,可以通过署理方式来处置处罚异常情况。
XCTWaiterDelegate:如果委托是XCTestCase实例,下方署理被调用时会报告为测试失败。
/// 异步测试XCTWaiter- (void)testAsyncMethod3 {    // 新建盼望    XCTWaiter *waiter = [[XCTWaiter alloc] initWithDelegate:self];    XCTestExpectation *expect3 = [[XCTestExpectation alloc] initWithDescription"asyncTest3"];            // 推行盼望:实行异步操纵    [ZJHNetworkTool requestUrl"getTestData" param:@{} completion:^(NSDictionary * respondDic) {        XCTAssertTrue([respondDic[@"code"] isEqualToString:@"200"]);        // 异步结束,标注盼望告竣        [expect3 fulfill];    }];    // 等待盼望被推行    XCTWaiterResult result = [waiter waitForExpectations:@[expect3]                                                 timeout:3                                            enforceOrder:NO];    XCTAssert(result == XCTWaiterResultCompleted, @"failure: %ld", result);}// 如果有盼望超时,则调用。- (void)waiter:(XCTWaiter *)waiter didTimeoutWithUnfulfilledExpectations:(NSArray<XCTestExpectation *> *)unfulfilledExpectations {    NSLog(@"***ZJH 如果有盼望超时,则调用。");}// 当推行的盼望被欺凌要求按次序推行,但盼望以错误的次序被推行,则调用。- (void)waiter:(XCTWaiter *)waiter fulfillmentDidViolateOrderingConstraintsForExpectation:(XCTestExpectation *)expectation requiredExpectation:(XCTestExpectation *)requiredExpectation {    NSLog(@"***ZJH 当推行的盼望被欺凌要求按次序推行,但盼望以错误的次序被推行,则调用。");}// 当某个盼望被标记为被倒置,则调用。- (void)waiter:(XCTWaiter *)waiter didFulfillInvertedExpectation:(XCTestExpectation *)expectation {    NSLog(@"***ZJH 当某个盼望被标记为被倒置,则调用。");}// 当 waiter 在 fullfill 和超时之前被打断,则调用。- (void)nestedWaiter:(XCTWaiter *)waiter wasInterruptedByTimedOutWaiter:(XCTWaiter *)outerWaiter {    NSLog(@"***ZJH 当 waiter 在 fullfill 和超时之前被打断,则调用。");}三、其他增补

3.1、断言记载

在写测试用例的时间,我们可以利用断言,下面是记载一下:
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 17:54, Processed in 0.170970 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表