iOS单元测试和集成测试

释放双眼,带上耳机,听听看~!

单元测试和集成测试

Unit: 单元测试,保证每一个类能够正常工作
UI: UI测试,也叫做集成测试,从业务层的角度保证各个业务可以正常工作。

0. 单元测试

准则:
保持测试的单一性
无耦合

概念:

在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试, 是针对程序模块的最小单位来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。 – 维基百科

优点:

避免低级错误
减少调试时间
增加可维护性
方便重构代码

缺点:

开发和维护成本高。
不能完全替代人工测试。

单元测试适合范围:

业务变动不频繁
业务属于核心功能

1. XCTest

- (void)setUp {
[super setUp];
//初始化的代码,在测试方法调用之前调用
}
- (void)tearDown {
// 释放测试用例的资源代码,这个方法会每个测试用例执行后调用
// 如测试踢人接口后,需要在结束时将对应成员拉回群里,以保证下次的单元测试能够正常进行。
[super tearDown];
}
// 判断这个类是否存在
- (void)testStackExist {
XCTAssertNotNil([TestStack class], @"不存在");
}
// 判断这类是否能创建
- (void)testStackCanCreate {
TestStack *stack = [TestStack new];
XCTAssertNotNil(stack, @"不能创建");
}
- (void)testPushANumberAndGetIt {
TestStack *stack = [TestStack new];
[stack push:2.0];
double topN = [stack pop];
XCTAssertEqual(topN, 2.0, @"怎么不相等呢");
//    [stack push:6.0];
//    double topN2 = [stack pop];
//    XCTAssertEqual(topN2, 2.0, @"怎么不相等呢");
}
- (void)testPerformanceExample {
// 测试性能例子
[self measureBlock:^{
// 需要测试性能的代码
}];
}
  1. 常用断言
 XCTFail(format…) 生成一个失败的测试;
XCTAssertNil(arg1, format...) 为空判断,arg1为空时通过,反之不通过;
XCTAssertNotNil(arg1, format…) 不为空判断,arg1不为空时通过,反之不通过;
XCTAssert(expression, format...) 当expression求值为TRUE时通过;
XCTAssertTrue(expression, format...) 当expression求值为TRUE时通过;
XCTAssertFalse(expression, format...) 当expression求值为False时通过;
XCTAssertEqualObjects(arg1, arg2, format...) 判断相等,[arg1 isEqual: arg2] 值为True时通过,其中一个不为空时,不通过;
XCTAssertNotEqualObjects(arg1, arg2, format...) 判断不等,[arg1 isEqual: arg2]值为False时通过,
XCTAssertEqual(arg1, arg2, format...) 判断相等(当arg1和arg2是 C语言标量、结构体或联合体时使用,实际测试发现NSString也可以);
XCTAssertNotEqual(arg1, arg2, format...) 判断不等(当arg1和arg2是 C语言标量、结构体或联合体时使用);
XCTAssertEqualWithAccuracy(arg1, arg2, accuracy, format...) 判断相等,(double或float类型)提供一个误差范围,当在误差范围(+/-accuracy)以内相等时通过测试;
XCTAssertNotEqualWithAccuracy(arg1, arg2, 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没有发生具体异常、具体异常名称的异常时通过测试,反之不通过

2. 第三方测试框架

mock 表示一个模拟对象, 它是对现有类的行为一种模拟(或是对现有接口实现的模拟)
stub 追踪方法的调用,在方法调用的时候返回指定的值。
mock 与 stub 最大的区别在于 stub 只是简单的方法替换,而不涉及新的对象,被 stub 的对象可以是业务代码中真正的对象。而 mock 行为本身产生新的(不可能在业务代码中出现的)对象,并遵循类的定义相应某些方法。

3. Kiwi

#import <Kiwi/Kiwi.h>
#import "YppNetworkService.h"
SPEC_BEGIN(YppNetworkServiceSpec)
describe(@"YppNetworkService", ^{
context(@"提审 环境测试: ", ^{
it(@"APP_STORE 应该 等于 1 ", ^{
[[theValue(APP_STORE) should] equal:theValue(1)];
});
it(@"ApiVersion 应该 等于 61 ", ^{
[[kApiVersion should] equal:@"61"];
});
it(@"app version  应该 等于 3.3.9 ", ^{
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *app_Version = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
[[app_Version should] equal:@"3.5.1"];
});
});
});
SPEC_END

describe : 行为描述(Specs), 描述需要测试的对象内容
context : 描述测试上下文
it 中的是测试的本体,描述了这个测试应该满足的条件, 实际的测试写在it里,是由一个一个的期望(Expectations)来进行描述的,期望相当于传统测试中的断言,要是运行的结果不能匹配期望,则测试失败

it有两个参数:

* 行为描述
* 行为的测试代码

三者共同构成了Kiwi测试中的行为描述。

其他关键词:

  • beforeAll(aBlock) – 当前scope内部的所有的其他block运行之前调用一次
  • afterAll(aBlock) – 当前scope内部的所有的其他block运行之后调用一次
  • beforeEach(aBlock) – 在scope内的每个it之前调用一次,对于context的配置代码应该写在这里
  • afterEach(aBlock) – 在scope内的每个it之后调用一次,用于清理测试后的代码
  • specify(aBlock) – 可以在里面直接书写不需要描述的测试
  • pending(aString, aBlock) – 只打印一条log信息,不做测试。这个语句会给出一条警告,可以作为一开始集中书写行为描述时还未实现的测试的提示。
  • xit(aString, aBlock) – 和pending一样,另一种写法。因为在真正实现时测试时只需要将x删掉就是it,但是pending语意更明确,因此还是推荐pending
  • 由于有context的存在,以及其可以嵌套的特性,测试的流程控制相比传统测试可以更加精确。我们更容易把before和after的作用区域限制在合适的地方。

在Kiwi中期望都由should或者shouldNot开头,并紧接一个或多个判断的的链式调用,大部分常见的是be或者haveSomeCondition的形式

注意:

    1. 对于 Kiwi 的 stub,需要注意的是它不是永久有效的,在每个 it block 的结尾 stub 都会被清空,超出范围的方法调用将不会被 stub 截取到。

2.

4. 让你的代码更容易单元测试

测试的准确性和工作量很大程度上依赖于开发人员的代码质量。

通常,为了单元测试的准确性,我们在写函数(方法)的时候会借鉴一些函数式编程的思想。其中最重要的一个思想就是 —- pure function(纯函数)

何为Pure function? 就是如果一个函数的输入一样,那么输出一定一样。

比如,这样的一个函数就不是pure function。因为它依赖于外部变量value的值。

static NSInteger value = 0;
- (NSInteger)function_1{
value = value + 1;
return value;
}

而这个函数就是pure function,因为给定输入,输出一定一致

- (NSInteger)function:(NSInteger)value{
value = value + 1;
return value;
}

如果你写了一个没有参数,或者没有返回值的方法,那么你要小心了,很可能这个方法很难测试。

5. code coverage 测试覆盖率

report -> by group -> coverage

6. 概念

TDD(Test Driven Development) – 测试驱动开发 是保证代码质量的不二法则, 是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷方法的核心实践,但不只适用于XP(Extreme Programming),同样可以适用于其他开发方法和过程。
TDD的基本思路就是通过测试来推动整个开发的进行,但测试驱动开发并不只是单纯的测试工作,而是把需求分析,设计,质量控制量化的过程。

BDD(Behavior-driven development) – 行为驱动开发。行为驱动开发简单来说就是先定义行为,然后定义测试用例,接着再编写代码。 实践中发现,通常没有那么多时间来先定义行为,不过BDD中的domain-specific language (DSL)能够很好的描述用例的行为。
BDD的框架让测试用例的目的更加明确,测试是否通过更加清晰
BDD的核心是行为。也就是说,需要关注的是一个类提供哪些行为

参考完档:
1. Kiwi 使用 –王巍
2. Kiwi 使用进阶 –王巍

人已赞赏
iOS文章

iOS项目中的知识点总结

2020-3-4 11:44:19

iOS文章

iOS聊天起泡(背景图片被拉伸不变形)

2020-3-4 12:55:28

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索