TestCleaner

通过移除样板代码,精简重复的 XCTests,让您可以专注于每个测试的输入和输出。

a photo-real 3D render of a fancy spray bottle. The label says TEST CLEANER and has artwork reminiscent of the Xcode icons for passing and failing tests.

这是一个现有的 XCTest,它通过将结果与已知的正确值进行比较,来确认 Version 类型的 ExpressibleByStringLiteral 是否正常工作

// Before
func testExpressibleByStringLiteral() {
  XCTAssertEqual("1", Version(major: 1)),
  XCTAssertEqual("1.0", Version(major: 1)),
  XCTAssertEqual("1.0.0", Version(major: 1)),
  XCTAssertEqual("1.2", Version(major: 1, minor: 2)),
  XCTAssertEqual("4.5.6", Version(major: 4, minor: 5, bugfix: 6)),
  XCTAssertEqual("10.1.88", Version(major: 10, minor: 1, bugfix: 88)),
}

使用 TestCleaner,我们可以在不牺牲 Xcode 高亮显示包含失败测试的行的能力的前提下,稍微清理一下这段代码。 我们还建议使用本地 typealias 来帮助减少代码噪音

// After
func testExpressibleByStringLiteral() {
  typealias V = Version
  assertEqual(testCases: [
    Pair("1", V(major: 1)),
    Pair("1.0", V(major: 1)),
    Pair("1.0.0", V(major: 1)),
    Pair("1.2", V(major: 1, minor: 2)),
    Pair("4.5.6", V(major: 4, minor: 5, bugfix: 6)),
    Pair("10.1.88", V(major: 10, minor: 1, bugfix: 88)),
  ])
}

现在,断言操作(“等于”)只需要在一个地方编写,使测试块不易出错并且意图更清晰。 它还减少了行长度,尽管 typealias 在这里有所帮助。

聚焦或跳过测试

借鉴 Quick 的语法,您可以通过在开头添加 f 来聚焦任何测试,并且只有这些测试将在下次运行时执行,允许您调试单个案例,而无需随意注释和取消注释行

func testExpressibleByStringLiteral() {
  typealias V = Version
  assertEqual(testCases: [
    Pair("1", V(major: 1)),
    fPair("1.0", V(major: 1)), // Only this test will run.
    Pair("1.0.0", V(major: 1)),
  ])
}

您还可以通过在测试用例前面加上 x 来跳过它们

func testExpressibleByStringLiteral() {
  typealias V = Version
  assertEqual(testCases: [
    Pair("1", V(major: 1)),
    xPair("1.0", V(major: 1)), // These last two tests will be ignored.
    xPair("1.0.0", V(major: 1)),
  ])
}

这些可以在单个测试中组合使用以增加灵活性:使用 xPair 首先跳过一些测试,然后专注于其中一个以使用 fPair 进行调试。

所有可用比较器

TestCleaner 镜像了 XCTAssert 提供的全部功能

自定义断言

如果您有自定义断言函数,可以使用 assertCustom 函数将其与 TestCleaner 一起使用。 只需确保您的自定义断言接受 file: StaticString, line: UInt 参数,并将 assertCustom 中的 tests 闭包传递给您的参数。

assertCustom(
  testCases: [
    Pair(someLeftValue, someRightValue),
    Pair(anotherLeftValue, anotherRightValue),
  ],
  tests: { pair, file, line in
    myCustomAssertion(
      try pair.left, try pair.right,
      message: pair.message,
      file: file, line: line // <-- ⚠️ this is important!
    )
    try youCanAlsoThrowErrorsInHere() // They will also get attributed to the correct line.
  }
)

何时使用 TestCleaner

此工具非常适合纯转换的测试,其中特定的输入将始终产生相同的输出。 示例包括:解析、映射、转换和计算。 它不适用于集成式测试,其中每个断言之前都有许多行设置代码。

深入阅读

TestCleaner 的灵感来自一篇 博客文章,该文章又受到与 Brian King 对话的启发。

喷雾瓶图像是使用 Blender 制作的,项目文件托管在 ZevEisenberg/TestCleanerIcon