随着项目增加的功能越来越多,渐渐的有一个问题是我们不能不考虑的,就是每次当我们增加了新功能,或者对某些地方做了优化,改版的时候,会不会影响到其他部分代码的正常运行,该如何才能避免每次进行大量的回归测试?这时候单元测试就很有用了。
介绍
在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
单元测试可以让我们在重构优化代码的时候及时发现错误。
分类
一般iOS的单元测试分为内置的 XCTest
和一些第三方的测试框架。XCTest
主要分为两部分Unit Test
(单元测试) 和 UI Test
(UI 测试)。
第三方的测试框架就比较多了,单元测试比较好的有Kiwi
、、
Quick
,UI测试有 KIF
、appium
。
目前我们只介绍iOS 内置的单元测试,第三方的测试框架有兴趣可以自己研究下。
XCTest使用
要使用XCTest需要我们在项目开始的时候选择引入单元测试的文件
如果新建工程的时候没有选择单元测试也没关系,可以在之后添加单元测试的target
进去
引入之后我们来看一下单元测试相关的方法
使用@testable import
可以引入要测试的模块,即使是私有的方法也是可以调用的。
// 每次测试前调用,可以测试前创建一些测试需要用到的对象
func setup()
// 每次测试结束时调用
func tearDown()
// 测试用例,一般我们不直接在这个方法里边写测试用例,而是以 `test` 为开头创建一个测试方法
func testExample()
// 用来测试性能
func testPerformanceExample()
测试断言
大多时候我们都使用断言决定测试的结果,可以使用断言进行比较、判断表达式真假,强行失败等:
//通用断言
XCTAssert(expression: Bool, message: String)
//常用断言:
XCTAssertTrue(expression: Bool, message: String)
XCTAssertFalse(expression: Bool, message: String)
XCTAssertEqual(expression1: Equatable, expression2: Equatable, message: String)
XCTAssertNotEqual(expression1: Equatable, expression2: Equatable, message: String)
XCTAssertEqualWithAccuracy(expression1: FloatingPoint, expression2: FloatingPoint, accuracy: FloatingPoint, message: String)
XCTAssertNotEqualWithAccuracy(expression1: FloatingPoint, expression2: FloatingPoint, accuracy: FloatingPoint, message: String)
XCTAssertNil(expression: Any?, message: String)
XCTAssertNotNil(expression: Any?, message: String)
XCTFail(message: String) //直接Fail的断言
当我们想要测试一个方法有没有达到我们的预期的是时候:
这里我有一个testInitializationCity()
的用例,是想确定manager
属性初始化后它的currentCity
属性是不是正确的。
当我们写好测试的代码的时候就可以运行下看看测试是否能通过
直接点击小方块就可以运行单个的测试用例
或者直接在导航栏操作点击右侧的运行按钮
异步测试
当我们要测试一个异步的方法的时候,结果不能立即获得,这时候我们可以使用设置期望,然后设定等待时间的方式。
当异步操作完成,并且满足我们的期望的时候,可以使用 exp.fulfill()
来表示满足期望。如果不满足就会抛出我们设定的描述
还可以通过expectationForNotification
方法,来监听一个通知,如果在规定时间内正确收到通知则测试通过。
性能测试
性能测试主要使用func measure(_ block: @escaping () -> Swift.Void)
方法,用于测试一组方法的执行时间,通过设置baseline(基准)和stddev(标准偏差)来判断方法是否能通过性能测试。
我们通过设置 Baseline
来设置基准时间,然后Max STDDEV
设置误差,在基准时间满足误差范围,就会测试通过。
项目使用CocoaPods管理时,单元测试报错解决
当在项目中使用CocoaPods时,使用单元测试时,必须为单元测试的框架也引入这些库,例如
# Uncomment the next line to define a global platform for your project
platform :ios, '8.1'
source 'http://repo.baichuan-ios.taobao.com/baichuanSDK/AliBCSpecs.git'
source 'https://github.com/CocoaPods/Specs.git'
source 'http://112.124.41.46/yougoods-ios/xgn.git'
target 'VLY' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for VLY
pod 'HandyJSON', '~> 1.6.1'
pod 'Alamofire', '~> 4.4.0'
pod 'FMDB', '~> 2.6.2'
pod 'CryptoSwift’, '~> 0.6.8'
pod 'SAMKeychain', '~> 1.5.2'
pod 'MJRefresh', '~> 3.1.12'
pod 'SnapKit', '~> 3.0.2'
pod 'AMap3DMap-NO-IDFA', '~> 4.6.1'
pod 'AMapLocation-NO-IDFA', '~> 2.3.1'
pod 'Kingfisher', '~> 3.6.0'
pod 'AliyunOSSiOS', '~> 2.6.0'
pod 'UTDID', '~> 1.1.0.16'
pod 'UMengAnalytics-NO-IDFA', '~> 4.2.5'
pod 'tingyunApp', '~> 2.5.4'
pod 'MBProgressHUD', '~> 1.0.0'
pod 'Vine', '= 1.0.6'
pod 'MediaPicker', '~> 1.1.4.5'
pod 'FuckSwiftGen', '~> 1.0.0'
target 'VLYTests' do
inherit! :search_paths
pod 'tingyunApp', '~> 2.5.4'
# Pods for testing
end
target 'VLYUITests' do
inherit! :search_paths
# Pods for testing
end
end
为VLYTests
target 引入这些第三方库,可以使用inherit! :search_paths
的方式继承,但是有些库继承会失败,所以这时候继承失败的库就要单独再写出来引入了。
以上就是单元测试的基本使用方法,单元测试学会使用并不难,但是想要用好,就需要多下些功夫了,是相对比较难的,而且可能需要耗费蛮多时间,反正如果单元测试能用好,对我们项目的好处是很大的。