在Swift中完善自定义断言

单元测试很棒,不是吗? 它不仅可以帮助您改进代码,而且可以确保下次更改它时不会弄乱任何东西。 测试的不利之处在于编写测试很容易,最终导致阅读困难! 在这里,我将介绍一些有用的技巧,这些技巧可以使您更容易阅读和理解单元测试代码。

断言及其使用

如果您之前没有编写过测试,则可能没有看到典型的测试断言。 断言(就像软件开发中的许多事物一样!)具有多种形状和大小,但是大多数情况下,它们是一段检查(或断言 )某些东西的代码。

if语句实际上是一个断言,在下面的示例中,我们断言该数组不为空:

 如果imagesArray.isEmpty == false { 
//在这里做点事
}

在测试中,断言是单元测试的关键点-我们使用它们来断言 (或检查)我们的逻辑是否为我们提供了预期的结果:

  func testTwoMultipliedByTwoIsFour(){ 
让计算器= Calculator()
 让结果= Calculator.multiply(2,by:2) 
  XCTAssertEqual(结果,4) 
}

在上述情况下,行XCTAssertEqual(result, 4)是断言—我们断言结果是4。

您可以在这里找到每个XCTAssert方法的完整描述。 在下面的演示中,我们可以看到样本测试失败时的外观。

您可以使用XCTFail()始终触发失败-在这种情况下,如果我们有一个if let可能无法通过,这很有用。 重要的一点是Xcode中的测试默认通过 ,因此我们必须记住在计为失败的分支上显式失败。

但是,这里的断言虽然很详细,但占用的行数却是其余测试的两倍! 让我们提取一个自定义断言以使其更具可读性。

定制断言

我们可以编写自己的自定义断言,这在断言可能变得复杂或最终导致在测试之间复制断言并实践三击规则的情况下非常有用。

我们可以提取断言,如下所示。 这使断言在测试方法本身中更小,并且还使我们对实际检查的内容有了更好的了解,而不必费力检查两个if let语句。

我们的断言现在可以正常工作。 但奇怪的是,断言失败不再显示在测试中,而是显示在我们的辅助方法中。 在运行测试时,这很烦人,因为您必须多花一些时间才能发现错误所在。 当多个测试使用相同的断言时,这确实很烦人,因为它们都在同一行上使断言失败!

我们想在调用自定义断言的地方显示失败,但是当前在我们的自定义断言的XCT函数显示失败。 幸运的是,我们可以改变这一点。

有些人会更喜欢在其自定义断言函数前加上XCT前缀,以使其看起来像内置断言方法,例如XCTAssertEquals(_,_) 。 通常不建议使用其他框架的前缀,但可以帮助您更快地在自动完成结果中找到自定义断言。 寻找最适合您的团队的东西。

更改显示断言的位置

我们有一种方法可以指出断言失败,使其看起来像来自调用自定义断言方法的位置,而不是来自其内部。

每个XCT函数都带有两个方便的默认参数-用于指示将红叉放在Xcode中的位置以及在哪里显示错误消息。 它们是fileline ,并且有两个对应的特殊“宏” * #file#line 。 这些宏保存使用时捕获的文件名和行号。 由于它们是默认参数,除非覆盖它们,否则它们将与调用XCT函数的文件和行相对应。

我们可以在自定义断言方法中使用它们,并将它们传递给XCT函数。 让我们将声明的签名更改为:

  func assertFirstSource(of imageSources:[ImageSource],isNetworkWithURL期望URL:URL,文件:StaticString = #file,行:UInt = #line) 

然后捕获该行并在其上调用我们的自定义断言(即,在我们的测试方法中,在下图中的第21行),然后将这些值注入XCT函数,以便它们发出的任何消息都将被发布到该行21:

注意我们现在如何将fileline传递给XCT方法调用。 现在,我们看到断言失败再次返回到测试中(这使测试如何失败更加明显),但是具有从XCTFail在第34行生成的消息。 我们在assertFirstSource(..)方法中设置了fileline默认参数,因此,如果将此方法包装在另一个自定义声明中,则可以执行相同的操作。

加起来

自定义断言是使测试更易于阅读的有用方法。 为了使它们正常工作:

  • 使用fileline及其相应的宏#file#line ,使测试失败出现在正确的位置。
  • 始终在每个分支中调用XCT函数( XCTAssertXCTFail ),否则测试可能会在失败时通过。
  • 为断言提供最清晰的错误消息,因此很明显为什么失败了。

谢谢阅读! 跟着我,继续听更多有关干净编码和改进Swift😇的信息。

可以在 我的Github 上找到此博客文章的代码