iOS_单元测试一之UnitTests

2022-07-20 14:47:57 浏览数 (1)

文章目录
  • 一、测试用例设计
    • 1、编写步骤
      • 1、Arrange(准备)
      • 2、Action(调用)
      • 3、Assert(断言)
    • 2、设计经验和原则
  • 二、创建测试文件
  • 三、断言方法
    • 1、布尔值断言
    • 2、空和非空断言
    • 3、相等和不想等断言
    • 4、可比值断言
    • 5、异常断言
    • 6、无条件测试失败
    • 7、跳过测试
    • 8、异步测试
  • 四、查看单测覆盖率
  • 五、性能测试
  • 六、其他
    • 1、为测试添加全局断点:
    • 2、测试项目里的文件
    • 3、私有属性和方法:
    • 4、添加测试`Target`方法:

一、测试用例设计

1、编写步骤

1、Arrange(准备)

准备好所需要的外部环境,如数据、mock等。

2、Action(调用)

调用需要测试的方法或流程。

3、Assert(断言)

判断调用返回的结果是否符合预期。 例如:

代码语言:javascript复制
func testDescForCount() {
    // Arrange (准备)
    let count = 0
    // Action (调用)
    let desc = MOUitl.descForCount(count);
    // Assert (断言)
    XCTAssertEqual(desc, "0")
}

2、设计经验和原则

  • 正面测试、负面测试、特性测试、完善代码覆盖率
  • 基于意图,而不是基于实现
  • 简单、清晰、易懂(包括函数名和函数体)
  • 避免引入条件判断、循环等逻辑
  • 纯UI描述不需要写单元测试
  • 数据逻辑需要写单元测试
  • 复杂代码需要进行合理的拆分
  • 通过单元测试优化代码架构

二、创建测试文件

一般来说,我们会为一个类or一个类型的功能创建一个测试类,继承自XCTestCase

下面来看一下这个类的几个方法:

代码语言:javascript复制
override class func setUp() {} // 类的setUp方法,在所有方法执行之前执行
override class func tearDown() {} // 类的tearDown,在所有方法执行之后执行
// 在每个测试方法执行之前都会执行,用于对每个测试方法都需要做的初始化操作(有异常时会抛出:适用于初始化会有异常抛出的情况)
override func setUpWithError() throws {}
override func setUp() {} // 同上(执行顺序在setUpWithError之后)适用于初始化无异常抛出的情况
override func tearDown() {} // 同下(执行顺序在tearDownWithError之前)适用于清理无异常抛出的情况
// 在每个测试方法执行之后都会执行,用于对每个测试方法都需要做的清理操作(有异常时会抛出:适用于清理会有异常抛出的情况)
override func tearDownWithError() throws {}
// 自定义两个测试方法:
func testFuncation1() {}
func testFuncation2() {}

加上log后,看一下执行顺序如下:

注意:测试方法必须是以test为前缀的,否则无法测试

三、断言方法

1、布尔值断言

Boolean Assertions

  • XCTAssert:断言为 true
  • XCTAssertTrue:断言为 true
  • XCTAssertFalse:断言为 false

例如:

代码语言:javascript复制
// 断言为选中状态
XCTAssert(self.vc.subscribeButton.isSelected)
// 断言为选中状态
XCTAssertTrue(self.vc.subscribeButton.isSelected)
// 断言为未选中状态
XCTAssertFalse(self.vc.subscribeButton.isSelected)

2、空和非空断言

Boolean Assertions

  • XCTAssertNil:断言为nil
  • XCTAssertNotNil:断言不为nil

例如:

代码语言:javascript复制
// 断言button为nil
XCTAssertNil(self.vc.subscribeButton)
// 断言button为不为nil
XCTAssertNotNil(self.vc.subscribeButton)

3、相等和不想等断言

Equality and Inequality Assertions

  • XCTAssertEqual:断言两个对象相等
  • XCTAssertNotEqual:断言两个对象不相等

例如:

代码语言:javascript复制
// 断言 两个对象 相等
XCTAssertEqual(self.subscribeButton, self.vc.subscribeButton)
// 断言 两个对象 不相等
XCTAssertNotEqual(self.subscribeButton, self.subscribeButton2)

4、可比值断言

Comparable Value Assertions

  • XCTAssertGreaterThan:断言大于某个值
  • XCTAssertGreaterThanOrEqual:断言大于等于某个值
  • XCTAssertLessThanOrEqual:断言小于等于某个值
  • XCTAssertLessThan:断言小于某个值

例如:

代码语言:javascript复制
// 断言num2大于num1
XCTAssertGreaterThan(self.vc.num2, self.vc.num1)
// 断言num2大于等于num1
XCTAssertGreaterThanOrEqual(self.vc.num2, self.vc.num1)

// 断言num1小于num2
XCTAssertLessThan(self.vc.num1, self.vc.num2)
// 断言num1小于等于num2
XCTAssertLessThanOrEqual(self.vc.num1, self.vc.num2)

5、异常断言

NSException Assertions

  • XCTAssertThrowsError:断言会抛出异常
  • XCTAssertNoThrow:断言不会抛出异常

例如:

代码语言:javascript复制
XCTAssertNoThrow(self.vc.viewDidLoad())
XCTAssertThrowsError(self.vc.viewDidLoad())

6、无条件测试失败

NSException Assertions

  • XCTFail:立即无条件生成一个失败

例如:

代码语言:javascript复制
XCTFail()

7、跳过测试

Skipping Tests

  • XCTSkipIf:如果条件为false,继续执行测试
  • XCTSkipUnless:如果条件为true,继续执行测试
  • XCTSkip:抛出跳过执行Error

举例:

代码语言:javascript复制
func testSkipping() throws {
    guard self.vc.isCanTests else {
        throw XCTSkip("Can't test vc")
    }
    try XCTSkipIf(!self.vc.isCanTests, "Can't test vc")
    try XCTSkipUnless(self.vc.isCanTests, "Can't test vc")
    XCTAssert(self.vc.isCanTests)
}

8、异步测试

明确是否需要验证异步逻辑 Asynchronous Tests

  • XCTestExpectation:期望
  • XCTWaiter:等待n个期望

举例:

代码语言:javascript复制
// 为异步下载任务创建一个期望
let expectation = XCTestExpectation(description: "Download apple.com home page")
let url = URL(string: "https://apple.com")!
let dataTask = URLSession.shared.dataTask(with: url) { (data, _, _) in
    // 断言下载数据不为nil
    XCTAssertNotNil(data)
    // 完成预期
    expectation.fulfill()
}
dataTask.resume() // 开始下载任务
// 等待:知道完成预期 or 超时
wait(for: [expectation], timeout: 3.0) // 超时时间不要设置过长
// 失败情况1:下载的data为nil
// 失败情况2:下载任务在3s内未完成

四、查看单测覆盖率

需要在Edit Scheme -> Test -> Options -> Code Coverage -> 勾上,才能看得到:

查看位置:

还有编辑器右边也能查看当前测试是否执行该行代码,或者是执行了几遍,显示位置如下图:

五、性能测试

measure闭包可以测试其括号内代码的性能:执行时长

代码语言:javascript复制
func testPerformanceExample() throws {
    measure {
        for _ in 0...1000 {
            MOPerson(name: "momo", age: 18)
        }
    }
}

这样写完会提示:No baseline average for Time,此时需要设置一下对该段代码期望的运行时间,设置方式如下图:

设置完之后,再测试一遍该方法,就能看到满足期望的比例了:


六、其他

1、为测试添加全局断点:

2、测试项目里的文件

Swift项目,当我们需要使用一个类时,Xcode会报找不到类型的错:Cannot find type 'MOTestsViewController' in scope 需要做的: 步骤1:需要在Target -> Build Phases -> Compile Sources 中点击 ,加入你需要测试的文件,如下图:

步骤2:在测试文件的头部导入目标项目,例如:

代码语言:javascript复制
@testable import MOSurveySwift

3、私有属性和方法:

  • Swift 无法测试私有属性和方法:(以下是官网Tips

Note: @testable provides access only for internal functions; file-private and private declarations are not visible outside of their usual scope when using @testable.

  • OC`的私有属性和方法,可以在当前测试用分类再次声明一下就可以测试了

4、添加测试Target方法:

方法一:在创建项目时勾选 方法二:在导航栏的测试tab添加:如图

Demo github地址

参考:官方文档

0 人点赞