本文介绍: 单元测试运行验证一段代码(称为“单元”)以确保其按预期运行并符合其设计自动化测试

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

一、单元测试简介

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

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)

原文地址:https://blog.csdn.net/guoxulieying/article/details/131431209

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_27986.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注