XCTAssertThrowsError由于引发错误而失败。 XCTAssertThrowsError失败:在JSON写入(NSObject)中抛出无效的类型

时间:2019-11-21 17:40:34

标签: swift xctest

我有一个从字典创建JSON数据的函数,并指定了它throws以便将错误传播到堆栈:

func createBodyDataFrom(dictionary: [String: Any]) throws -> Data {
        let bodyData = try JSONSerialization.data(withJSONObject: dictionary, options: [])

        return bodyData
    }

但是根据XCode,使用XCTAssertThrowsError进行测试时,我遇到了测试失败,因为该函数引发了异常

func testCreateJSONFrominValidDictionaryThrows() {
        let validDictionary: [String: Any] = [
            "object": NSObject()
        ]
        XCTAssertThrowsError(try testClient.createBodyDataFrom(dictionary: validDictionary))
    }

IDE的失败消息显示:

  

XCTAssertThrowsError失败:在JSON写入(NSObject)中抛出无效类型

这似乎是矛盾的,但是如果不测试失败案例就感到不完整。有什么想法怎么了吗?

1 个答案:

答案 0 :(得分:-1)

让我们从在Objective-C中运行一些等效的代码开始,以便我们在这里了解Cocoa的作用:

NSDictionary* d = @{@"Howdy": [NSObject new]};
NSError* err;
[NSJSONSerialization dataWithJSONObject:d options:0 error:&err];
NSLog(@"%@", err);

如果这是导致NSError的事情,我们将能够读取err的值并将其打印到控制台。但这不是,我们不能。我们从未接触过NSLog语句。而是,程序意外崩溃:

Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: 'Invalid type in JSON write (NSObject)'
    1   libobjc.A.dylib  0x00007fff50b97b20 objc_exception_throw + 48
    2   Foundation       0x00007fff2580cb68 -[_NSJSONWriter writeRootObject:toStream:options:error:] + 0
    3   Foundation       0x00007fff2580ff03 ___writeJSONObject_block_invoke + 371
[and so on]

那不是 一个NSError。这是一个NSException。它们是完全不同的东西,基本上这意味着我们的程序崩溃了。这不是错误的一掷千金。是突然死亡。

这就是documentation警告我们会发生的事情。如果我们提供了无法转换为JSON的对象,则dataWithJSONObject:options:error这个方法不会失败。而是崩溃了:

  

如果obj将不会产生有效的JSON,则会引发异常。此异常是在解析之前抛出的,它表示 programming 错误,而不是 internal 错误。 在使用isValidJSONObject:调用此方法之前,应检查输入是否会生成有效的JSON

(斜体字。)

因此,发现问题并防止崩溃的正确方法是先致电isValidJSONObject: dataWithJSONObject:options:error这个方法不会解决这类问题,只会返回NSError;它将杀死整个程序。

这就是发生的事情。您对try JSONSerialization.data(withJSONObject:)的呼叫不是 throw。它只是崩溃了。您可以看到,如果您在实际的Swift应用程序中单独尝试它:

override func viewDidLoad() {
    super.viewDidLoad()
    do {
        try testCreateJSONFrominValidDictionaryThrows()
    } catch {
        print("oops")
    }
}
func createBodyDataFrom(dictionary: [String: Any]) throws -> Data {
    let bodyData = try JSONSerialization.data(withJSONObject: dictionary, options: [])
    return bodyData
}
func testCreateJSONFrominValidDictionaryThrows() throws {
    let validDictionary: [String: Any] = [
        "object": NSObject()
    ]
    try createBodyDataFrom(dictionary: validDictionary)
}

我们运行该应用程序,会发生什么?我们不打印“哎呀”。相反,我们得到与Objective-C完全相同的崩溃。

您的案例也是如此。我们从来没有投掷,因为我们坠毁了。测试失败报告了这一事实。您的testClient从您的身下摔了下来(也许您可以通过一些控制台操作来检测到此情况),并且由于我们从未抛出NSError,所以测试本身又以失败告终。


在这里,可可引发NSException和返回NSError之间的区别至关重要。只有后者认为Swift会抛出错误。前者是崩溃。例如参见

How can I throw an NSError from a Swift class and catch it in an Objective-C class?

Avoid handling all exceptions until they've reached a global handler

How to catch NSUnknownKeyException in swift 2.2?