iOS开发——单元测试

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012411480/article/details/51803083

单元测试

闻名不如见面!在某技术交流群里问了下iOS开发的人,貌似写单元测试的程序猿不多,查了些资料写了demo,发现单元测试还是挺有用的,第一次用就确实测试出了运行成功语法没错,但是存在问题的方法!初次见面还是印象不错,那还是从概念开始深入认识吧

一.概念

单元测试又称模块测试,属于白盒测试,是最小单位的测试。模块分为程序模块和功能模块。功能模块指实现了一个完整功能的模块(单元),一个完整的程序单元具备输入、加工和输出三个环节。而且每个程序单元都应该有正规的规格说明,使之对其输入、加工和输出的关系做出名明确的描述。

覆盖率:代码的覆盖程度,一种度量方式。针对代码的测试覆盖率有许多种度量方式,定义如下:
语句覆盖(StatementCoverage):也称为行覆盖(linEC),段覆盖(segmentcoverage)和基本块覆盖(bAS)。它度量每一个可执行语句是否被执行到了。icblockcoverageoverage
判定覆盖(DecisionCoverage):也被称为分支覆盖(branchcoverage),所有边界覆盖(all-edgescoverage),基本路径覆盖(basispathcoverage),判定路径覆盖(decision-decision-path或DDPtesting)。它度量是否每个BOO型的表达式取值true和false在控制结构中都被测试到了。

二.测试内容

单元测试的对象是软件设计的最小单位——模块或函数,单元测试的依据是详细设描述。测试者要根据详细设计说明书和源程序清单,了解模块的I/O条件和模块的逻辑结构。主要采用白盒测试的测试用例,辅之以黑盒测试的测试用例,使之对任何合理和不合理的输入都能鉴别和响应。要求对所有的局部和全局的数据结构、外部接口和程序代码的关键部分进行桌面检查和代码审查。在单元测试中,需要对下面5个方面的内容进行测试,也是构造测试用例的基础。

1.模块接口

正确地输入和输出:

(1)模块及其子模块,输入及其输出的行参实參在个数、属性、顺序上是否匹配;

(2)全局变量的定义在各模块中是否一致;

(3)限制是否通过形式参数来传送。

2.局部数据结构测试

(1)检查不正确或不一致的数据类型说明;

(2)使用尚未赋值或尚未初始化的变量;

(3)错误的初始值或错误的默认值;

(4)变量名拼写错误或书写错误;

(5)不一致的数据类型。

3.路径测试

对基本执行路径和循环进行测试,查找错误计算及其错误控制流

常见的不正确的计算有:

(1)运算的优先次序不正确或误解了运算的优先次序;

(2)运算的方式错误(运算的对象彼此在类型上不相容);

(3)算法错误;

(4)初始化不正确;

(5)运算精度不够;

(6)表达式的符号表示不正确等。

常见的比较和控制流错误有:

(1)不同数据类型的比较;

(2)不正确的逻辑运算符或优先次序;

(3)因浮点运算精度问题而造成的两值比较不等;

(4)关系表达式中不正确的变量和比较符;

(5)“差1错”,即不正确地多循环或少循环一次;

(6)错误的或不可能的循环终止条件;

(7)当遇到发散的迭代时不能终止循环;

(8)不适当地修改了循环变量等。

4.错误处理测试

预见出错条件,并设置适当的出错处理对策。确保逻辑正确性。

错误情况:

(1)出错的描述难以理解;

(2)出错的描述不足以对错误定位和确定出错的原因;

(3)显示的错误与实际的错误不符;

(4)对错误条件的处理不正确;

(5)在对错误进行处理之前,错误条件已经引起系统的干预;

如果出错情况不予考虑,那么检查恢复正常后模块可否正常工作。

5.边界测试

设计测试用例检查:

(1)在n次循环的第0次、1次、n次是否有错误;

(2)运算或判断中取最大最小值时是否有错误;

(3)数据流、控制流中刚好等于、大于、小于确定的比较值时是否出现错误。

三.测试环境

测试时间:编码阶段。
测试用例设计:利用软件设计文档,设计可以验证程序功能、找出程序错误的多个测试用例。

对于每一组输入,应该有预期的正确结果。在单元测试时,如果模块不是独立的程序,需要辅助测试模块,有两种辅助模块:

驱动模块(Driver):所测模块的主程序。它接收测试数据,把这些数据传递给所测试模块,最后再输出测试结果。当被测试模块能完成一定功能时,也可以不要驱动模块。

桩模块(Stub):用来代替所测模块调用的子模块。
被测试模块、驱动模块和桩模块共同构成了一个测试环境

四.测试方法

用例设计

1.测试用例的组成(在单元测试中测试用例基本上由测试脚本组成)

用例运行前置条件

被测模块/单元所需环境(全局变量赋值或初始化实体)

启动测试驱动

设置桩

调用被测模块

设置预期输出条件判断

恢复环境(包括清除桩)

2.测试用例的设计原则

一个好的测试用例在于能够发现至今没有发现的错误;

测试用例应由测试输入数据和与之对应的预期输出结果这两部分组成;

在测试用例设计时,应当包含合理的输入条件和不合理的输入条件;

为系统运行起来而设计测试用例;

为正向测试而设计测试用例;

为逆向测试而设计测试用例;

为满足特殊需求而设计测试用例;

为代码覆盖而设计测试用例;

3.用例设计方法

1) 规范(规格)导出发

2) 等价类划分法

3) 边界值分析法

4) 状态转移测试法

5) 分支测试法

6) 条件测试法

7) 数据定义-使用测试法(又名数据流测试法)

8) 内部边界值测试法

9) 错误猜测法

  1. 特定的用例测试设计

1)声明测试:检查模块中的所有变量是否被声明。经验表明,大量重要的错误都是由于变量没有被声明或没有被正确的声明而引起的。

2)路径测试:要求模块中所有可能的路径都被执行一遍,属逻辑覆盖测试。

基本路径测试:由于实际中,一个模块中的路径可能非常多,由于时间和资源有限,不可能一一测试到。这就需要把测试所有可能路径的目标减少到测试足够多的路径,以获得对模块的信心。要测试的最小路径集就是基本测试路径集。基本测试路径集要保证:

每个确定语句的每一个方向要测试到;
每条语句最少执行一次。

3)循环测试:重点检查循环的条件-判断部分以及边界条件。测试循环是一种特殊的路径测试,因为循环比其他语句都复杂一些。循环中错误的发生机会比其他代码构成部分多。因此,对于任何给定的循环测试应该包括测试下面每一条件的测试用例:

循环不执行;执行一次循环;执行两次循环;

反映执行典型的循环的执行次数;
如果有最大循环次数,最大循环次数减1;
最大循环次数;
大于最大循环次数。

对于增量和减量不是1的FOR语句,要特别注意,因为程序员习惯于增量1。

4) 循环嵌套:循环嵌套使逻辑的次数呈几何级数增长,设计测试嵌套循环的测试用例应该包括的测试条件有:

把外循环设置为最小值,并运行内循环所有可能的情况;
把内循环设置为最小值,并运行外循环所有可能的情况;
把所有的循环变量都设置为最小值运行;
把所有的循环变量都设置为最大值运行;
把外循环设置为最大值,并运行内循环所有可能的情况;
把内循环设置为最大值,并运行外循环所有可能的情况;

5) 边界值测试:指程序内部边界测试。检查确定代码在任何边界情况下都不会出差错。重点检查小于、等于和大于边界条件的情况。边界值测试是指专门设计用来测试当条件语句中引用的值处在边界或边界附近时系统反映的测试。

6)接口测试:检查模块的数据流(输入、输出)是否正确。检查输入的参数和声明的自变量的个数,数据类型和输入顺序是否一致。检查全局变量是否被正确的定义和使用等。

7)确认测试:是否接受有效输入数据(操作),拒绝无效数据(操作)。

8)事务测试:输入->输出,错误处理。

五.测试优化

  • 优化测试用例:用例的合并、修改和删除,减少冗余的无价值的测试,其优化依据来源于测试后的测试数据分析和评估,其中测试覆盖也是用例优化的主要参考。
  • 优化测试执行:测试步骤的优化,减少测试人员的手工操作,因为太多的手工操作会导致测试人员很厌倦,直接影响测试效果,优化依据来源于测试总结。
  • 优化策略:按优先级测试:优先级划分依据:重点模块;最复杂,易出错;相对独立,可提前测试;容易扩散错误;开发者没有信心的模块;

80-20原则:80%的缺陷聚集在20%的模块中,经常出错的模块改错后还会经常出错,这种应该列入测试重点。

六.测试评估

1.测试用例完备性

2.代码覆盖率评估,主要是根据代码覆盖率工具提供的语句覆盖情况报告

3.覆盖:
功能覆盖;输入域覆盖;输出域覆盖;函数交互覆盖;代码执行覆盖

七.测试实施过程

  1. 单元测试实施步骤

1) 制定测试计划和测试方案(包括测试工具的选择)

2) 根据计划和方案及相关输入文档编写测试用例

3) 搭建测试环境

4) 执行测试

5) 记录和跟踪问题

6) 编写测试报告和总结报告

2.测试工具

  • 使用Xcode自带单元测试UnitTest
    。 Xcode单元测试包含在一个 XCTestCase 的子类中

特点:

不需要其他软件;使用简单;

可以功能测试

      - (void)testGetCurrentVC{

        UIViewController *currentVC = [self.mainPageVC getCurrentVC];

        XCTAssertNotNil(currentVC,"The result of test is  get currentVC");
       // XCTAssertNil(currentVC,"The result of test is get currentVC");

    }

性能测试:

    - (void)testPerformanceExample {

    [self measureBlock:^{
        for (int i = 0; i < 100; i ++) {

            [self.mainPageVC qiangdanTixingView:@"testQiangDan" titleText:@"包车订单" playTime:@"20160623" playPeopleNum:@"3" playType:@"包车出游" money:@"300" dingdanNum:@"131325465"];
        }
    }];

}

异步测试:借助 XCTestExpectation 类来实现。现在,测试能够为了确定的合适的条件等待一个指定时间长度,而不需要求助于GCD。

开始测试

1.添加tests ,创建工程是勾选UnitTest或者手动创建 手动添加testTarget

2.鉴定测试通过:打开工程中”show the test navigator”
这里写图片描述

可以向下一级级展开。长按小三角会运行测试

3.Write tests
例:测试某个控制器。先import导入,定义一个VC属性,实例化,调用该VC的方法,写断言提示。(一些断言语句写在本文最后),测试方法写在那里不用调用

4.Write code that passes the tests

5.run :commond + u 或者produce –> test

[测试HomeViewControl控制器的getcurrent方法]
这里写图片描述

  • OCMock

OCMock是一个用于为iOS或Mac OS X项目配置Mock测试的开源项目,如果目标是iOS项目那么生成的是静态库,如果是Mac OS X项目生成的是框架。
其实现思想就是根据要mock的对象的class来创建一个对应的对象,并且设置好该对象的属性和调用预定方法后的动作(例如返回一个值,调用代码块,发送消息等等),然后将其记录到一个数组中,接下来开发者主动调用该方法,最后做一个verify(验证),从而判断该方法是否被调用,或者调用过程中是否抛出异常等。

github地址

  • GHUnit
    需要手动配置,麻烦些;具有GUI,功能强大;上传却到12年没有继续了
    github地址

断言

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和XCTAssertEqual。

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

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

对于后者,如果a1和a2都是基本数据类型变量,那么只有a1 == a2才会返回YES。

阅读更多 登录后自动展开
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页