iOS UnitTest 学习 (二) 了解单元测试的机制,以及如何写好单元测试

前言

除了断言,还有更多的测试。XCTest 什么时候创建和运行测试?iOS程序员特别容易对测试生命周期做出错误的假设。这些假设会导致测试设计中的错误。

比如我们经常遇到的,一个测试用例,在单独运行的情况下可以测试通过,但是在项目组合测试中失败,为了避免不稳定的测试,我们需要给测试一个稳定,干净的环境.

测试方法中的代码组织成三个部分来明确这些阶段:

  • 安排、Arrange 安排,它是定义所有变量和模型的部分。
  • 行动、Action 行为:是触发被测方法并返回结果的部分。
  • 断言` Assert 断言:这是评估预期结果的部分。

记住AAA 是单元测试的重要部分

XCTest的使用

   func test_methodOne() {
        let sut = MyClass()

        sut.methodOne()

        // Normally, assert something
    }

名称sut代表被测系统,通常缩写为SUT。这是“我们正在测试的东西”的常用术语。与这个简单的例子不同,测试通常有很多对象在起作用。使用像sut这样一致的名称可以清楚地表明测试将作用于哪个对象。它还可以更轻松地重用测试代码片段。

XCTest 测试机制

  • XCTest 在测试时候,搜索从XCTestCase继承的所有类。
  • 对于这样的类,它会找到每个测试方法。这些方法的名称以test开头,没有参数,也没有返回值。如func test_methodOne()
  • 对于每个这样的测试方法,它都会创建一个类的实例。使用 Objective-C runtime,它会记住该实例将运行哪个测试方法。
  • XCTest 将子类的实例收集到测试套件中。
  • 当它完成所有测试用例的创建后,XCTest 才会开始运行它们。
class MyClassTests: XCTestCase {
    private let sut = MyClass()

    func test_methodOne() {
        sut.methodOne()

        // Normally, assert something
    }

    func test_methodTwo() {
        sut.methodTwo()

        // Normally, assert something
    }
}
举例

MyClassTests 有两个测试方法 test_methodOnetest_methodTwo
那么,在XCTest运行时, 会找到MyClassTests。它搜索以test开头的方法名称,并找到两个。所以它创建了MyClassTests 的两个实例:一个实例运行test_methodOne,另一个运行test_methodTwo

使用setUp(),tearDown()优化

XCTestCase定义了两个方法,setUp和tearDown,这两个方法设计为在子类中被覆盖。

XCTest 中的测试运行程序保证每个测试用例的顺序如下:

  • 呼叫的setUp(创建对象)。
  • 调用测试方法。
  • 调用tearDown(销毁)。
class MyClassTests: XCTestCase {
    private var sut: MyClass!

    override func setUp() {
        super.setUp()
        sut = MyClass()
    }

    override func tearDown() {
        sut = nil
        super.tearDown()
    }

    func test_methodOne() {
        sut.methodOne()

        // Normally, assert something
    }

    func test_methodTwo() {
        sut.methodTwo()

        // Normally, assert something
    }
}

注意: 初始化存储属性的测试类中。要将这些属性从let更改为var。并且添加

Tips 检测测试Log

通过如图所示,我们可以快速找到test的失败消息


截屏2021-07-07 下午8.03.37.png

如何写好项目里的测试?

代码测试覆盖率

在 Xcode 菜单中,选择ProductSchemeEdit Scheme...或按 - <

截屏2021-07-07 下午9.13.20.png

现在我们就设置好了测试覆盖率

+ U 试一下

截屏2021-07-07 下午9.21.27.png

并且 Xcode 菜单中选择Editor ▶ Code Coverage。

截屏2021-07-07 下午9.23.06.png

红色区域的数字代表我们这段代码测试了几次,在红色条纹区域。将鼠标光标悬停在该区域,您会看到情况发生了变化,如下所示:

截屏2021-07-07 下午9.24.42.png

绿色部分显示了我们接触过的代码。带有else 的那一行部分是绿色的,部分是红色的。这为我们提供了一种查看行内代码覆盖率的方法。

为现有代码添加测试

举例

class CoveredClass {

    static func max(_ x: Int, _ y: Int) -> Int {
        if x < y {
            return y
        } else {
            return x
        }
    }
}

如果代码正在使用中,我们就不需要从需求逆向工作。相反,我们可以编写有效地使用遗留代码称为特性测试的内容。这些是捕获代码实际行为的测试。

要编写特性测试,请执行以下操作:

  • 从测试中调用代码,产生某种结果。
  • 编写一个断言,将结果与您知道不匹配的值进行比较。
  • 运行测试。失败消息将告诉您实际结果。
  • 调整断言,使其预期实际结果。
  • 重新运行测试以查看它是否通过。

1,2部分

    func test_max_with1And2_shouldReturnSomething() {
        let result = CoveredClass.max(1, 2)

        XCTAssertEqual(result, -123)
    }
截屏2021-07-07 下午9.33.33.png

3.运行测试。这给了我们一条失败消息

4.我们从失败消息中复制实际值2并粘贴到断言中

    func test_max_with1And2_shouldReturn2() {
        let result = CoveredClass.max(1, 2)

        XCTAssertEqual(result, 2)
    }

5.运行

截屏2021-07-07 下午9.37.40.png

我们本次的测试就完成了!!! 通过~

当然,如果你想要更好的测试覆盖率,让我们添加一个测试来覆盖后半部分。
条件是if x < y

    func test_max_with3And2_shouldReturn3() {
        let result = CoveredClass.max(3, 2)

        XCTAssertEqual(result, 3)
    }

这应该给我们 100% 的覆盖率。但是实际上,会由于有个},很难做到100%覆盖率

截屏2021-07-07 下午9.45.53.png

多写几个,也是可以完成100% 覆盖率的,但是,我们不应该成为测试覆盖率的奴隶.

Tips

一个好的测试名称包含三个部分:

  • 测试的内容是什么。这通常是一个函数名。
  • 测试条件。有什么不同的输入?
  • 预期的结果。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容