在 Swift 中测试断言 [英] Testing assertion in Swift

查看:24
本文介绍了在 Swift 中测试断言的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为具有断言的方法编写单元测试.Swift 语言指南建议对无效条件"使用断言:

I'm writing unit tests for a method that has an assertion. The Swift Language guide recommends using assertions for "invalid conditions":

断言会导致您的应用终止,并且不能替代以不太可能出现无效条件的方式设计您的代码出现.尽管如此,在无效条件的情况下可能,断言是确保这种情况的有效方法条件在开发过程中被突出显示并注意到应用已发布.

Assertions cause your app to terminate and are not a substitute for designing your code in such a way that invalid conditions are unlikely to arise. Nonetheless, in situations where invalid conditions are possible, an assertion is an effective way to ensure that such conditions are highlighted and noticed during development, before your app is published.

我想测试失败案例.

但是,Swift 中没有 XCTAssertThrows(从 Beta 6 开始).我如何编写单元测试来测试断言是否失败?

However, there is not XCTAssertThrows in Swift (as of Beta 6). How can I write an unit test that tests that an assertion fails?

编辑

根据@RobNapier 的建议,我尝试将 XCTAssertThrows 包装在 Objective-C 方法中并从 Swift 调用此方法.这不起作用,因为宏没有捕获由 assert 引起的致命错误,因此测试崩溃.

As per @RobNapier's suggestion, I tried wrapping XCTAssertThrows in an Objective-C method and calling this method from Swift. This doesn't work as the macro does not catch the fatal error caused by assert, and thus the test crashes.

推荐答案

assert 和它的兄弟 precondition don't throw exceptions cannot be "catch" (even with Swift2 的错误处理).

assert and its sibling precondition don't throw exceptions cannot be "caught" (even with Swift 2's error handling).

您可以使用的一个技巧是编写自己的插入式替换,它执行相同的操作,但可以替换以进行测试.(如果您担心性能,只需 #ifdef 将其用于发布版本.)

A trick you can use is to write your own drop-in replacement that does the same thing but can be replaced for tests. (If you're worried about performance, just #ifdef it away for release builds.)

/// Our custom drop-in replacement `precondition`.
///
/// This will call Swift's `precondition` by default (and terminate the program).
/// But it can be changed at runtime to be tested instead of terminating.
func precondition(@autoclosure condition: () -> Bool, @autoclosure _ message: () -> String = "", file: StaticString = __FILE__, line: UWord = __LINE__) {
    preconditionClosure(condition(), message(), file, line)
}

/// The actual function called by our custom `precondition`.
var preconditionClosure: (Bool, String, StaticString, UWord) -> () = defaultPreconditionClosure
let defaultPreconditionClosure = {Swift.precondition($0, $1, file: $2, line: $3)}

测试助手

import XCTest

extension XCTestCase {
    func expectingPreconditionFailure(expectedMessage: String, @noescape block: () -> ()) {

        let expectation = expectationWithDescription("failing precondition")

        // Overwrite `precondition` with something that doesn't terminate but verifies it happened.
        preconditionClosure = {
            (condition, message, file, line) in
            if !condition {
                expectation.fulfill()
                XCTAssertEqual(message, expectedMessage, "precondition message didn't match", file: file.stringValue, line: line)
            }
        }

        // Call code.
        block();

        // Verify precondition "failed".
        waitForExpectationsWithTimeout(0.0, handler: nil)

        // Reset precondition.
        preconditionClosure = defaultPreconditionClosure
    }
}

示例

func doSomething() {
    precondition(false, "just not true")
}

class TestCase: XCTestCase {
    func testExpectPreconditionFailure() {
        expectingPreconditionFailure("just not true") {
            doSomething();
        }
    }
}

(要点)

当然,类似的代码也适用于 assert.然而,由于您正在测试行为,您显然希望它成为您的接口合同的一部分.您不希望优化代码违反它,并且 assert 将被优化掉.所以最好在这里使用 precondition.

Similar code will work for assert, of course. However, since you're testing the behavior, you obviously want it to be part of your interface contract. You don't want optimized code to violate it, and assert will be optimized away. So better use precondition here.

这篇关于在 Swift 中测试断言的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆