Swift 单元测试入门

news2024/11/19 3:29:47

含义:编程语言中的单元测试是为了确保编写的代码按预期工作。
给定一个特定的输入,希望代码带有一个特定的输出。通过测试代码,能够给当前的重构和发布建立信心,因为将能够确保代码在成功运行的测试套件后按预期工作。

一、单元测试简介

单元测试是运行和验证一段代码(称为“单元”)以确保其按预期运行并符合其设计的自动化测试。
例如,写一个字符串扩展方法将第一个字母大写:

extension String {
    func uppercasedFirst() -> String {
        let firstCharacter = prefix(1).capitalized
        let remainingCharacters = dropFirst().lowercased()
        return firstCharacter + remainingCharacters
    }
}

我们要确保 uppercasedFirst()方法按预期工作。如果我们给它一个输入 antoine,我们期望它输出 Antoine。我们可以使用XCTAssertEqual 方法为此方法编写单元测试:

final class StringExtensionsTests: XCTestCase {
    func testUppercaseFirst() {
        let input = "antoine"
        let expectedOutput = "Antoine"
        XCTAssertEqual(input.uppercasedFirst(), expectedOutput, "The String is not correctly capitalized.")
    }
}

如果我们的方法不再按预期工作(比如上面的扩展代码不小心被修改了),Xcode 将使用我们提供的描述显示失败:
在这里插入图片描述

二、项目中添加单元测试

  1. 创建项目时勾选单元测试
    在这里插入图片描述

  2. 已有项目添加测试target
    左下角添加target,搜索test,选择Unit Test Bundle
    在这里插入图片描述
    在这里插入图片描述

  3. 项目中就会出现单元测试的文件夹
    在这里插入图片描述

三、在 Swift 中编写单元测试

有多种方法可以测试相同的结果,但是当测试失败时它并不总是给出相同的反馈。以下提示可帮助您编写测试,通过从详细的失败消息中获益,帮助您更快地解决失败的测试。

1.命名测试用例和方法

描述你的单元测试是很重要的,这样你就会明白测试试图验证什么。如果你不能想出一个简短的名字,那你可能测试了太多东西。一个好名字还可以帮助您更快地解决失败的测试。

要快速找到特定类的测试用例,建议使用相同的命名并结合 “test”。就像上面的例子一样,我们根据我们正在测试一组字符串扩展的事实命名了 StringExtensionTests。如果您正在测试ContentViewModel 实例,另一个示例可能是 ContentViewModelTests。

2.不要所有测试都使用 XCTAssert

许多场景都可以使用 XCTAssert,但当测试失败时会导致不同的结果。以下代码行都测试了完全相同的结果:

func testEmptyListOfUsers() {
    let viewModel = UsersViewModel(users: ["Ed", "Edd", "Eddy"])
    XCTAssert(viewModel.users.count == 0)
    XCTAssertTrue(viewModel.users.count == 0)
    XCTAssertEqual(viewModel.users.count, 0)
}

正如你所看到的,该方法使用了一个描述性的名字,告诉人们要测试一个空的用户列表。然而,我们定义的视图模型不是空的,因此,所有的断言都失败了。
在这里插入图片描述

结果显示了为什么必须对验证类型使用正确的断言。 XCTAssertEqual 方法为我们提供了有关断言失败原因的更多上下文。这显示在红色错误和控制台日志中,可帮助您快速识别失败的测试。

3.Setup and Teardown

多个测试方法中使用的参数可以定义为测试用例类中的属性。您可以使用 setUp() 方法为每个测试方法设置初始状态,并使用 tearDown() 方法进行清理。有多种设置和拆卸方法的变体供您选择,例如支持并发的变体或抛出变体,如果设置失败,您可以在其中提前使测试失败。
一个可以生成用户默认实例以用于单元测试的示例:

struct SearchQueryCache {
    var userDefaults: UserDefaults = .standard

    func storeQuery(_ query: String) {
        /// ...
    }
}

final class SearchQueryCacheTests: XCTestCase {

    private var userDefaults: UserDefaults!
    private var userDefaultsSuiteName: String!

    override func setUpWithError() throws {
        try super.setUpWithError()
        userDefaultsSuiteName = UUID().uuidString
        userDefaults = UserDefaults(suiteName: userDefaultsSuiteName)
    }

    override func tearDownWithError() throws {
        try super.tearDownWithError()
        userDefaults.removeSuite(named: userDefaultsSuiteName)
        userDefaults = nil
    }

    func testSearchQueryStoring() {
        /// 使用生成的用户默认值作为输入。
        let cache = SearchQueryCache(userDefaults: userDefaults)

        /// ... write the test
    }
}

这样做可以确保您不会操纵在模拟器上测试期间使用的标准用户默认值。其次,您将确保在测试开始时处于干净状态。我们使用了拆卸方法来删除用户默认套件并进行相应的清理。

4.抛出方法

和编写应用程序代码时一样,您也可以定义一个可抛出测试的方法。这允许您在测试中的方法抛出错误时使测试失败。例如,在测试 JSON 响应的解码时:

func testDecoding() throws {
    /// 当数据初始值设定项抛出错误时,测试将失败。
    let jsonData = try Data(contentsOf: URL(string: "user.json")!)

    /// `XCTAssertNoThrow` 可用于获取有关抛出的额外上下文
    XCTAssertNoThrow(try JSONDecoder().decode(User.self, from: jsonData))
}

当在任何进一步的测试执行中不需要 throwing 方法的结果时,可以使用 XCTAssertNoThrow 方法。您应该使用 XCTAssertThrowsError 方法来匹配预期的错误类型。例如,您可以为证书密钥验证程序编写测试:

struct LicenseValidator {
    enum Error: Swift.Error {
        case emptyLicenseKey
    }

    func validate(licenseKey: String) throws {
        guard !licenseKey.isEmpty else {
            throw Error.emptyLicenseKey
        }
    }
}

class LicenseValidatorTests: XCTestCase {
    let validator = LicenseValidator()

    func testThrowingEmptyLicenseKeyError() {
        XCTAssertThrowsError(try validator.validate(licenseKey: ""), "An empty license key error should be thrown") { error in
            /// 我们确保预期的错误被抛出。
            XCTAssertEqual(error as? LicenseValidator.Error, .emptyLicenseKey)
        }
    }

    func testNotThrowingLicenseErrorForNonEmptyKey() {
        XCTAssertNoThrow(try validator.validate(licenseKey: "XXXX-XXXX-XXXX-XXXX"), "Non-empty license key should pass")
    }
}

5.可选值解包

XCTUnwrap 方法最适合用于抛出测试,因为它是一个抛出断言:

func testFirstNameNotEmpty() throws {
let viewModel = UsersViewModel(users: [“Antoine”, “Maaike”, “Jaap”])

let firstName =  try XCTUnwrap(viewModel.users.first)
XCTAssertFalse(firstName.isEmpty)

}
XCTUnwrap 断言可选变量的值不为 nil,如果断言成功则返回它的值。它会阻止您编写 XCTAssertNotNil 并结合解包或处理其余测试代码的条件链接。

四、在 Xcode 中运行单元测试

编写测试后,就该运行它们了。通过以下提示,这将变得更有效率。

1.使用测试三角形

您可以使用前导三角形运行单个测试或一组测试:
在这里插入图片描述

根据最新的测试运行结果,同一方块显示红色或绿色。

2.重新运行最新的测试

使用以下命令重新运行上次运行测试:

⌃ Control + ⌥ Option + ⌘ Command + G.

上面的快捷方式可能是我最常用的快捷方式之一,因为它可以帮助我在对失败测试实施修复后快速重新运行测试。

3. 运行测试组合

使用 CTRL 或 SHIFT 选择要运行的测试,右键单击并选择“Run X Test Methods”。
在这里插入图片描述

4.在测试导航器中应用过滤器

在这里插入图片描述

  • 使用搜索字段根据名称搜索特定测试
  • 仅显示当前所选方案的测试。如果您有多个测试方案,这将很有用。
  • 只显示失败的测试。这将帮助您快速找到失败的测试

五、问题统计

1. 运行单元测试后代码签名失败

应用程序已经打开了自动签名,所以我认为当测试目标也没有打开自动签名时,Xcode 中出现了问题。代码签名需要一致。
在这里插入图片描述

2. 在单元测试中引用Framework

头部添加包名称@testable import (name)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/694561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

macos中回退键是Command+shift+z,我该如何改成Command+y?

macos中回退键是Commandshiftz,我该如何改成Commandy? 操作如下,打开系统设置-键盘 在「菜单标题」中手动输入需要更改快捷键的菜单项名称,注意要完全一致(见下图)。 最后说一句,撤销与重做是非常基础的系…

VS2017编译64位库出现问题解决方法

1、问题:VS2017编译32位Release库正常通过,但是64位库总是报错 解决方法:添加setupapi.lib到依赖项中

线性代数笔记整理

文章目录 1 行列式2 矩阵(本质是数表)3 方程组的解4 向量5 矩阵的特征值和特征向量6 相似矩阵和相似对角化7 合同对角化8 二次型及其标准型 1 行列式 2 矩阵(本质是数表) 3 方程组的解 4 向量 5 矩阵的特征值和特征向量 6 相似矩阵…

【MATLAB第47期】基于MATLAB的多卷积层的卷积神经网络MCNN分类预测模型,含交叉验证,可自定义层数

【MATLAB第47期】基于MATLAB的多卷积层的卷积神经网络MCNN分类预测模型,含交叉验证,可自定义层数 一、展示效果 依次对比卷积层数为1/2/3时的分类预测结果 可得出,随着卷积层数量增加,训练集/测试集正确率基本上得到改进。 1.一…

GPIO模拟时序控制外设4——红外发射管

文章目录 前言红外发射管简介NEC协议HS0038NEC 的逻辑“1”与逻辑“0”NEC的数据帧格式 编程思路1. GPIO管脚2. 模拟同步头3.发送逻辑“0”与逻辑“1”发送一个字节数据发送一帧数据结束码现象 总结 前言 上一篇介绍了使用GPIO模拟时序实现I2C协议的功能,本文继续使…

DELL戴尔笔记本电脑灵越Inspiron 14 54105418原厂Win10系统恢复原装OEM出厂状态系统

Dell戴尔笔记本电脑,灵越Inspiron 14 5410&5418原装出厂OEM系统镜像原厂系统文件 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件等预装程序 链接:https://pan.baidu.com/s/1Qj_dW5lj71e9d71-je4dXw?pwdz3z1 提取码:z3z1

Oracle Recovery Tools恢复csc higher than block scn---惜分飞

有客户强制关闭数据库,结果有数据块报坏块,dbv检查为:csc higher than block scn问题 该问题主要是由于scn异常导致通过Oracle Recovery工具进行修复 dbv再次验证数据块ok,Oracle Recovery完美代替bbed解决该问题 通过OraRecovery工具快速解决csc higher than block scn故障…

IntelliJ IDEA如何自动生成serialVersionUID

IntelliJ IDEA如何自动生成serialVersionUID? 实体对象在流中传输时,需要将其序列化。 对象的类型实现Serializable接口public class ClassName implements java.io.Serializable { } 生成版本号serialVersionUID单击类名,按Alt Enter,在出…

C语言的##使用

##在C/C中具有连接字符串的作用 #include<stdio.h> #define TEST(_name, _inst_alloc) \printf("token6 %d\n",_name##_inst_alloc); int main() {int token6 100;TEST(token, 6);return 0; }

[环境配置][原创]VS2019新建项目一直打转转圈很久才能正常显示模板项目

不知道什么时候VS2019新建一个C#或者C项目越来越慢&#xff0c;点击新建项目一直是下面状态 我记得以前最多等个几秒都是可以出来的&#xff0c;后面发现要一分钟以上。这个确实令人抓狂。于是找到一种方法可以明显改善这个问题&#xff0c;那就是卸载Nsight相关程序。一般都是…

软考高级系统架构设计师(四) 计算机网络3物联网云计算

目录 物联网 关键技术 云计算 物联网 PS&#xff1a;可能下午题 关键技术 射频识别、二维码&#xff08;感知层&#xff09; 云计算

为什么游戏总是闪退?游戏闪退的原因和解决方法

在玩游戏的过程中&#xff0c;会有游戏突然关闭的情况&#xff0c;游戏闪退后回到电脑桌面或游戏主界面&#xff0c;十分影响游戏体验。端午开黑在际&#xff01;驱动人生游戏助你攻克游戏闪退&#xff0c;带来游戏闪退的原因和解决方法。 **1、设备性能不足&#xff1a;**某些…

ElasticSearch-IK分词器介绍和下载

IK分词器 什么是IK分词器&#xff1f; 分词:把一段中文或者别的划分成一个一个的关键字,我们在搜索的时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如"我爱魏一鹤"会被分成&quo…

hive引入外部函数-java实现

引入依赖 <dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec</artifactId><version>3.1.2</version> </dependency>编写函数 注意必须要继承GenericUDF 类 import org.apache.hadoop.hive.ql.exec.UDF…

5.按键输入

1.按键介绍&#xff1a; 按键WK_UP接在低电平&#xff0c;当按键按下检测到高电平&#xff1b;KEY0和KEY1接在高电平&#xff0c;当按键按下检测到低电平&#xff1b; 2.本次实验步骤&#xff1a; &#xff08;1&#xff09;GPIO输入操作&#xff1a; 读取IO口输入电平的库…

vue中实现将html导出为word文档

需求&#xff1a;将页面整成理想样式&#xff0c;将想要的那一部分页面导出成word,不用写模板&#xff0c;按照当前页面样式导出即可。&#xff08;简易版&#xff09; 保姆级别教程&#xff1a; 第一步&#xff1a;安装需要的依赖 npm install html-docx-js -Snpm install …

第11讲:使用ajax技术实现文件上传功能(jQuery)

软件项目开发过程中,文件上传功能是常用技术之一,经常需要上传文件过程中为了更好的体验,不允许刷新当前页面,这样就必须使用异步上传技术了,jQuery提供了异步上传的多种方法,下面跟大家一起探讨使用jQuery.form插件上传文件的功能,具体实现过程如下: 环境要求 序号资…

【单片机】MSP430 单片机 多路温度巡回检测仪的设计

多路温度巡回检测仪的设计 多路温度检测仪共有3个测温点&#xff0c;每个点连续检测8次&#xff0c;以平均值代表该点温度&#xff0c;并同时在 LCD 显示器&#xff0e;器任选&#xff0c;测量精度为1℃。系统每隔1秒完成一个点的测量。 设计以msp430g2553为核心的多路温度检测…

深度学习笔记之一看数据

最近在百度ai上跟了个“深度学习7日打卡营”的课程&#xff0c;目前看到人脸关键点检测章节&#xff0c;地址如下&#xff1a;飞桨AI Studio - 人工智能学习与实训社区 (baidu.com) 老师们讲解的很好&#xff0c;但是对于我这样的白菜来说&#xff0c;有些细节还是感觉略过去了…

Java-多线程

进程与线程 程序>进程>线程 程序是一段静止的代码&#xff0c;只有真正运行时的程序&#xff0c;才被称为进程。一个程序运行至少有一个进程 从操作系统底层来说&#xff0c;进程只是一个概念&#xff0c;真正执行的是线程。 进程是操作系统资源分配的基本单位&#x…