Swift 单元测试入门

这篇具有很好参考价值的文章主要介绍了Swift 单元测试入门。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

一、单元测试简介

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

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 将使用我们提供的描述显示失败:
Swift 单元测试入门

二、项目中添加单元测试

  1. 创建项目时勾选单元测试
    Swift 单元测试入门

  2. 已有项目添加测试target
    左下角添加target,搜索test,选择Unit Test Bundle
    Swift 单元测试入门
    Swift 单元测试入门

  3. 项目中就会出现单元测试的文件夹
    Swift 单元测试入门

三、在 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)
}

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

结果显示了为什么必须对验证类型使用正确的断言。 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.使用测试三角形

您可以使用前导三角形运行单个测试或一组测试:
Swift 单元测试入门

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

2.重新运行最新的测试

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

⌃ Control + ⌥ Option + ⌘ Command + G.

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

3. 运行测试组合

使用 CTRL 或 SHIFT 选择要运行的测试,右键单击并选择“Run X Test Methods”。
Swift 单元测试入门

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

Swift 单元测试入门

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

五、问题统计

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

应用程序已经打开了自动签名,所以我认为当测试目标也没有打开自动签名时,Xcode 中出现了问题。代码签名需要一致。
Swift 单元测试入门

2. 在单元测试中引用Framework

头部添加包名称@testable import (name)文章来源地址https://www.toymoban.com/news/detail-515170.html

到了这里,关于Swift 单元测试入门的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Rust编程语言入门之高级特性

    不安全 Rust 高级 Trait 高级 类型 高级函数和闭包 宏 隐藏着第二个语言,它没有强制内存安全保证:Unsafe Rust(不安全的 Rust) 和普通的 Rust 一样,但提供了额外的“超能力” Unsafe Rust 存在的原因: 静态分析是保守的。 使用 Unsafe Rust:我知道自己在做什么,并承担相应风险

    2023年04月24日
    浏览(50)
  • Rust编程语言入门之智能指针

    指针:一个变量在内存中包含的是一个地址(指向其它数据) Rust 中最常见的指针就是”引用“ 引用: 使用 借用它指向的值 没有其余开销 最常见的指针类型 智能指针是这样一些数据结构: 行为和指针相似 有额外的元数据和功能 通过记录所有者的数量,使一份数据被多个

    2023年04月16日
    浏览(53)
  • Rust编程语言入门之无畏并发

    Concurrent:程序的不同部分之间独立的执行(并发) Parallel:程序的不同部分同时运行(并行) Rust无畏并发:允许你编写没有细微Bug的代码,并在不引入新Bug的情况下易于重构 注意:本文中的”并发“泛指 concurrent 和 parallel 在大部分OS里,代码运行在进程(process)中,OS同时

    2023年04月19日
    浏览(70)
  • C语言入门 Day_1 编程与C语言

    计算机最早的出现是为了帮人们解决一些计算量巨大的任务,比如计算 导弹的轨迹,原子弹的爆炸范围,火箭的速度等等特定目的。 最早的计算机,一种计算机只能处理一种特定任务,随着时代的发展,一个能处理多种不同的任务的计算机开始出现,这就是通用计算机,比

    2024年02月12日
    浏览(40)
  • APP开发入门:了解主流的编程语言

    在过去的几年里,有许多程序员开始学习和使用编程语言。这其中包括C、C++、 Java和 Python。尽管有许多语言可供选择,但大多数程序员都会选择最容易学习的编程语言。 如今,有很多编程语言供选择。程序员们在学习这些语言时可以自由地选择他们喜欢的方式,因为他们的

    2024年02月15日
    浏览(49)
  • Rust编程语言入门之函数式语言特性:-迭代器和闭包

    闭包(closures) 迭代器(iterators) 优化改善 12 章的实例项目 讨论闭包和迭代器的运行时性能 闭包:可以捕获其所在环境的匿名函数。 闭包: 是匿名函数 保存为变量、作为参数 可在一个地方创建闭包,然后在另一个上下文中调用闭包来完成运算 可从其定义的作用域捕获值

    2023年04月08日
    浏览(45)
  • C语言编程入门之刷题篇(C语言130题)(8)

    (题目标题可以直接跳转此题链接) BC72 平均身高   从键盘输入5个人的身高(米),求他们的平均身高(米)。 输入描述: 一行,连续输入5个身高(范围0.00~2.00),用空格分隔。 输出描述: 一行,输出平均身高,保留两位小数。 输入:1.68 1.75 1.82 1.60 1.92 输出:1.75  参考

    2024年02月02日
    浏览(35)
  • Rust编程语言入门之cargo、crates.io

    通过 release profile 来自定义构建 在https://crates.io/上发布库 通过 workspaces 组织大工程 从 https://crates.io/来安装库 使用自定义命令扩展 cargo release profile: 是预定义的 可自定义:可使用不同的配置,对代码编译拥有更多的控制 每个 profile 的配置都独立于其它的 profile cargo 主要的

    2023年04月09日
    浏览(55)
  • 初学编程入门基础教学视频,中文编程开发语言工具箱之豪华编辑构件,免费版中文编程软件下载

    初学编程入门基础教学视频,中文编程开发语言工具箱之豪华编辑构件,免费版中文编程软件下载  构件的其中一个属性、方法,查找内容,替换内容。 构件工具箱非常丰富,其中该构件在 文本件构件板菜单下。 编程系统化课程总目录及明细,零基础学中文编程视频教程,

    2024年02月07日
    浏览(81)
  • Shell编程——弱数据类型的脚本语言快速入门指南

    目录 Linux Shell 数据类型 变量类型 运算符 算术运算符 赋值运算符 拼接运算符 比较运算符 关系运算符 控制结构 顺序结构 条件分支结构 if 条件语句 case 分支语句  循环结构 for 循环 while 循环 until 循环 break 语句 continue语句 函数 函数定义  函数名 函数体 返回值 参数 函数的

    2024年02月12日
    浏览(77)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包