【发布时间】:2019-04-11 13:38:10
【问题描述】:
我想编写一些自定义测试断言类型并让它们显示在 Xcode 中,类似于 XCTAssert() 失败的显示方式:
有没有办法让我加入 Xcode 并实现这一点?
我希望我自己的断言函数在此处以同样的方式显示其错误:
到目前为止,我发现的最好的资源是 Apple 的 XCTest source code,但我无法理解它是否包含负责显示错误 UI 的逻辑。
【问题讨论】:
我想编写一些自定义测试断言类型并让它们显示在 Xcode 中,类似于 XCTAssert() 失败的显示方式:
有没有办法让我加入 Xcode 并实现这一点?
我希望我自己的断言函数在此处以同样的方式显示其错误:
到目前为止,我发现的最好的资源是 Apple 的 XCTest source code,但我无法理解它是否包含负责显示错误 UI 的逻辑。
【问题讨论】:
最简单的方法是从您的自定义断言中调用XCTFail(),但传递调用站点的文件名和行号。例如:
func verify(myThing: MyThing, file: StaticString = #filePath, line: UInt = #line) {
// Do your verification. Then when you want to fail the test,
XCTFail("Some message about \(myThing)", file: file, line: line)
}
在调用站点,您可以让默认参数提供file 和line。所以它看起来像:
verify(myThing: thing)
在 Swift 中,XCTest 断言是全局函数。这意味着您的助手也可以是一个全局函数,并在测试套件之间共享,而不必继承 XCTestCase。
【讨论】:
XCTFail 似乎使用的是#filePath 而不是#file。它可能也应该用作验证的默认参数。
完全可以做你想做的事,我只是设法做到了,像这样:
recordFailure
您可以通过在任何测试中调用recordFailure 来实现您想要的(继承自标准XCTestCase)。
XCTestCase
如果你想简化对这个函数的调用,你可以编写一个扩展函数来包装它。
你也可以继承XCTestCase(如果你想共享一些设置,这可能是个好主意,在setup中调用,那么你只需要在这个新的超类中对你的测试类进行)。
class TestCase: XCTestCase {
func forwardFailure(
withDescription description: String = "Something went wrong",
inFile filePath: String = #file,
atLine line: Int = #line,
expected: Bool = false
) {
self.recordFailure(
withDescription: description,
inFile: filePath,
atLine: line,
expected: expected
)
}
}
我不确定如何使用expected: Bool,它是recordFailure method (source) 的必需参数,但看起来Apple 大多将其设置为false。
现在您可以像这样声明您的自定义断言方法(或作为 XCTestCase 的扩展,取决于您的选择):
extension TestCase {
/// Since `Foobar.Type` does not conform to `Equatable`, even if `Foobar` conforms to `Equatable`, we are unable to write `XCTAssertEquals(type(of: foo), Foobar.self)`, this assert method fixes that.
func XCTAssertType<Actual, Expected>(
of actual: Actual,
is expectedType: Expected.Type,
_ filePath: String = #file,
_ line: Int = #line
) {
if let _ = actual as? Expected { return /* success */ }
let actualType = Mirror(reflecting: actual).subjectType
forwardFailure(
withDescription: "Expected '\(actual)' to be of type '\(expectedType)', but was: '\(actualType)'",
inFile: filePath,
atLine: line
)
}
}
现在它报告错误,不是在自定义断言中,而是在调用站点?。
如果您查看例如XCTAssertTrue,它接受一个 line 参数和一个 file 参数。
【讨论】:
使用Xcode 12,我们可以使用XCTIssue 内联断言
现有的recordFailure 已弃用。
下面是测试的例子
import XCTest
@testable import TestAssertionForward
class TestAssertionForwardTests: XCTestCase {
func testExample() throws {
assert(actual: 10, expected: 20) // asserts in method
assert(actual: 10, expected: 11) // asserts in method
assertInLine(actual: 10, expected: 11) // asserts in line
assertInLine(actual: 10, expected: 11) // asserts in line
}
}
extension TestAssertionForwardTests {
func assert(actual: Int, expected: Int) {
XCTAssertEqual(actual, expected)
}
func assertInLine(actual: Int, expected: Int, filePath: String = #file, lineNumber: Int = #line) {
if actual != expected {
let location = XCTSourceCodeLocation(filePath: filePath, lineNumber: lineNumber)
let context = XCTSourceCodeContext(location: location)
let issue = XCTIssue(type: .assertionFailure,
compactDescription: "\(actual) is not equal to \(expected)",
detailedDescription: nil,
sourceCodeContext: context,
associatedError: nil,
attachments: [])
record(issue)
}
}
}
下面是失败断言的截图
【讨论】: