跳到主要内容

使用 TDD 修复 Swift 中的错误

· 阅读需 8 分钟
GoSwiftUI
goswiftui.com

使用 TDD 修复 Swift 中的错误

测试驱动开发 (TDD) 是一种技术,要求你在开始实现解决方案之前先编写一个失败的测试。虽然开发人员在一般开发过程中会使用这种技术,但有一种方法可以仅将其应用于错误修复。

发现错误已经令人失望,但发现错误再次发生会更糟。防止错误在你修复后再次出现是一项重要的开发技能。

测试驱动开发 (TDD) 流程要求你在开始实现解决方案之前先编写一个失败的测试。你可以使用它来定义新功能的所有规范,并确保不会忘记任何预期结果。

例如,假设有以下文章结构:

struct Article {
let title: String
let author: String
let link: URL
}

我们可能希望创建一个新的 计算属性 来返回这篇文章的博客域。在开始编写实际计算属性之前,我们首先定义以下测试用例:

func testArticleDomain() {
let article = Article(
title: "Async await in Swift explained with code examples",
author: "Antoine van der Lee",
link: URL(string: "https://www.avanderlee.com/swift/async-await/")!
)

XCTAssertEqual(article.blogDomain, "avanderlee.com")
}

此代码不会编译,因为 blogDomain 属性不存在。由于我们希望测试首先失败,因此我们可以添加一个返回空字符串的计算属性:

extension Article {
var blogDomain: String {
""
}
}

我们现在可以成功运行单元测试并得出结论,它确实失败了:

使用测试驱动开发,我们首先编写了一个失败的测试。

使用测试驱动开发,我们首先编写了一个失败的测试。

最后,我们实现实际逻辑,并重新运行测试以确保它成功:

extension Article {
var blogDomain: String {
link.host()?.replacingOccurrences(of: "www.", with: "") ?? ""
}
}

工作流程看起来与许多开发人员所做的事情类似,但主要区别在于从编写测试开始,而不是直接进入功能开发。

TDD 的好处

开发人员往往犹豫在工作流程中开始使用测试驱动开发。编写一个失败的测试感觉多余,而且浪费时间。在我开始列出 TDD 的一些好处之前,我认为强调一下这一点很重要:当它适合你的当前任务时,你可以将此技术用作选择加入的工具集。

我在解决错误时偶尔会使用它,因为我想清除我对预期结果的思考。通过编写测试,你可以确保记住所有边缘情况和预期结果。其次,你将使自己能够完全专注于开发解决方案,而无需不断考虑必须支持的结果:你可以运行测试,并且知道当所有测试都成功时就完成了。

首先编写失败测试的另一个好处是确保你的测试实际上会失败。这听起来很愚蠢,但我看到许多测试即使实现发生变化也会始终成功。换句话说,测试不会捕获任何错误的实现。

最后,通过从测试开始,你将确保你的新代码从一开始就可以进行测试。当你编写没有考虑测试的新代码时,事后编写测试可能会更具挑战性。由于它变得更加困难,因此更容易完全跳过编写测试,从而导致测试覆盖率降低。

使用测试解决错误的双赢

我开始撰写有关测试驱动开发的主要原因是我在解决错误时主要使用它。

使用测试而不是手动交互来重现错误

当我开始解决错误时,我首先想知道如何重现它。你可能做的第一件事是打开你的应用程序并开始四处导航。然而,根据我的经验,查看特定代码并使用与错误报告中所述类似的输入编写测试要容易得多。理想情况下,你将有足够的日志(例如,通过使用 Diagnostics)来了解导致错误的原因。如果没有,你可以阅读特定代码行并使用测试来查找触发器。

编写测试的另一个好处是,你可能会更好地理解错误的原因。如果你能够更快地解决错误,我不会感到惊讶。因此,TDD 与错误修复配合得很好。

使用就位的测试解决错误

一旦你有了测试,你就可以开始解决错误。根据错误的不同,这可能具有挑战性且耗时。有一种方法可以快速验证你的解决方案可以极大地缩短修复时间。你不必手动执行各种步骤,而是可以简单地重新运行测试并进行验证。

我看到开发人员使用手动应用程序交互来解决错误,他们甚至无法在第一时间持续重现错误。换句话说,凭一点运气,你认为自己解决了错误,但实际上并没有。在你认为修复是可靠的之前,必须有一个持续的重现。

更高的代码覆盖率和更高的信心

一旦你解决了错误,你便同时实现了新测试并提高了代码覆盖率。你还可以对你的代码库更有信心,因为你已经确保错误无法在不使你的测试失败的情况下返回。代码库信心不容小觑,对于希望定期向大量受众发布更新的应用程序至关重要。

结论

测试驱动开发是一种通过同时提高测试代码覆盖率和代码质量来提高代码质量的出色技术。一开始编写失败的测试可能很乏味,但结果将是一个更完整的特性实现,涵盖所有预期结果。当与错误修复结合使用时,你将提高你的信心,并确保错误不会在将来再次出现。