【问题标题】:How to write a unit test in iOS for an expected assert/assertionFailure?如何在 iOS 中为预期的断言/断言失败编写单元测试?
【发布时间】:2015-08-03 14:53:17
【问题描述】:

更新:

看起来这个功能应该在即将到来的Swift 2.0/XCode 7 版本中得到支持,这显然将包括 try/catch 的优点,所以这个问题可能没有实际意义。当他们的测试版结束时,我会尝试相应地更新这篇文章。

原始问题:

在 Swift 中,虽然我假设问题/答案适用于 Objective-C,但我想编写一个函数 foo,格式为:

public class SomeClass{
  public func foo(someString:String){
    //validate someString
    assert(!someString.isEmpty, "The someString parameter cannot be empty.")
  }
}

我使用assert 调用,因为我相信这是 Apple 推荐的,而不是像其他语言中常见的抛出异常。

但是,在我的单元测试中,我希望能够确保当someString 参数为空字符串时函数确实失败:

class SomeClass_Tests:XCTestCase{
  func test_foo_someStringParamaterIsEmpty_error(){
    //ACTION
    let someClassInstance = SomeClass()
    someClassInstance.foo("")

    //VALIDATE
    //**What goes here?
  }
}

我找不到关于这种情况的文档或帖子,尽管我认为这是一个非常重要的单元测试,以确保类和库的正确行为和使用。

在包含异常/异常处理的其他语言中,assert 将被替换为 throw SomeError() 之类的东西,然后,在单元测试中,您可以简单地将操作包装在 try/catch 块中并断言确实设置了异常,如下所示:

class SomeClass_Tests:XCTestCase{
  func test_foo_someStringParamaterIsEmpty_error(){
    //ACTION
    let someClassInstance = SomeClass()

    var expectedException:SomeException? = nil
    try{
      someClassInstance.foo("")
    }catch(someException:SomeException){
      expectedException = someException
    }

    //VALIDATE
    XCTAssertIsNotNil(expectedException)
  }
}

但是我在文档中看到的 Swift 中没有这样的构造或等效的解决方法。是否有任何已知的解决方案或变通方法可用于执行此类测试?

【问题讨论】:

  • XCTest 应该有一个 did-assert 或 did-not-assert 断言是绝对正确的(尽管通过启用/禁用断言的构建配置会稍微复杂一些 - 对测试先决条件和致命错误更有用大概)。 Brian Gesiak 写了this post 关于一些限制并提交了this radar 专门关于断言。
  • 感谢@AirspeedVelocity,这些链接非常有用。我认为缺乏真正的“异常”是由于一些狂妄自大(让人想起初级开发人员将“抛出异常”等同于“抛出错误”),而对我来说,这很快成为我认为的痛处相当有趣的语言/框架。
  • 我对例外情况持谨慎态度。一方面,是的,能够捕获意外溢出会很好。另一方面,通常用于非真正异常原因的异常在其他语言中完全是诅咒......所以总而言之,我永远不会快乐......
  • 哦,是的,例外绝对是整个大国/大责任的经典案例。

标签: ios swift unit-testing assertions xctest


【解决方案1】:

Matt Gallagher 的 CwlPreconditionTesting project on github 添加了一个 catchBadInstruction 函数,使您能够在单元测试代码中测试断言/前置条件失败。

CwlCatchBadInstructionTests file 显示了其使用的简单说明。 (请注意,它仅适用于 iOS 模拟器。)

【讨论】:

【解决方案2】:

为什么要尝试捕获错误,而不是检查发生了什么而不是测试抛出的断言?

XCTAssertThrows()

and than 查看值 ?

【讨论】:

  • XCTAssertThrows 在 Swift 中不直接可用。
  • 从 Swift 2 开始出现 XCTAssertThrowsError,请参阅 stackoverflow.com/questions/36190161/…
  • XCTAssertThrows 是一个断言某事引发异常的函数。这与“捕获”assert 声明不同。
【解决方案3】:

您可以查看 XCTest 框架中可用的 XCTAssertThrowsXCTAssertThrowsSpecific 宏。

也许您需要将您的断言替换为 [NSException raise] 才能使其正常工作。

【讨论】:

  • 这些宏在 Swift 语言中不可用,所以我最初假设答案适用于 Objective-C 是不正确的。这个post 非常相似,提供了一些建议和信息。
  • 如果您确实需要测试断言,可以考虑将您的 XCTestCase 模块切换为 ObjC 语言。
  • 或者(也许不可能,我只写了 2 周的 swift)你可以尝试使用仅测试断言的特定方法将 ObjC 类别添加到 XCTestCase swift 模块。
【解决方案4】:

但是,在我的单元测试中,我希望能够确保当 someString 参数为空字符串时函数确实失败

我明白你想要做什么,因为我自己在 Ruby 程序中做过。但是 Swift(正如我经常观察到的那样)不是 Ruby!这里的问题是,当someString 参数为空字符串时,您可以保证函数不会 失败——在您的实际应用程序中。这是因为断言不在发布版本中操作

结果是你可以在开发过程中使用断言作为调试的一种形式,但如果这种情况在现实生活中可能发生,你应该有条不紊地处理它,而不是崩溃。

因此测试断言是否“发生”不是真正有效的单元测试技术,这就是为什么您无法以这种方式使用它。

【讨论】:

  • "但是如果这种情况在现实生活中可能发生,你应该有条不紊地处理它,而不是崩溃。" - 对我来说,错过了例外的意义。对我来说,例外情况是开发人员如何向其他开发人员(或他们自己)表明一个方法或类正在以一种信息性的方式被滥用,而不是仅仅在内部和不一致地崩溃。我认为它实际上在哲学上与! nilable 运算符背后的思路背道而驰。 “这是这个超级容易调试/导致生产崩溃的操作员,可以让事情变得更安全......而且......这就是你所得到的!走开!” :P
  • “对我来说错过了例外点”我完全同意;因此我对在 Ruby 中执行此操作的评论。但我认为 you 完全错过了 Swift 没有 具有 异常的意义! (至少,不是作为流控制的一种形式。是的,你仍然可以在运行时崩溃,怎么能拒绝?!!!)我并不是说我同意与 Swift 的哲学,但我我说这哲学,这就是为什么你不能在这里做你想做的事。
  • 异常是传达致命错误的常见 Objective-C 范式,可以预期与 Objective-C 库互操作的快速程序需要一种方法来处理该范式。
  • @emish 他们现在就做!但是当我写下那个答案时,他们没有回过神来。从那时起,Swift 发生了很大的变化。你在看历史。
猜你喜欢
  • 1970-01-01
  • 2017-04-10
  • 2021-04-14
  • 1970-01-01
  • 2020-01-03
  • 1970-01-01
  • 2011-06-11
  • 1970-01-01
相关资源
最近更新 更多