I'd like to write some custom test assertion types and have them be displayed in Xcode, similarly to how the XCTAssert() failures are displayed:
Is there a way for me to hook into Xcode and make this happen?
I want my own assertion function to show its error inline the same way here:
The best resource I've found so far is Apple's XCTest source code, but I haven't been able to understand if that even includes the logic that is responsible for displaying the error UI.
The easiest way is to call XCTFail()
from your custom assertion, but pass along the file name and line number of the call site. For example:
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)
}
At the call site, you'd let the default arguments provide file
and line
. So it would just look like:
verify(myThing: thing)
In Swift, XCTest assertions are global functions. This means your helper can also be a global function, and shared across test suites without having to subclass XCTestCase
.
It is possible to do exactly what you want, I just managed to do it, like this:
recordFailure
You can achieve what you want just by calling recordFailure
, in any test (inheriting from standard XCTestCase
).
XCTestCase
If you want to simplify the call to this function you either write an extension function wrapping it.
You can also subclass XCTestCase
(which can be a good idea if you want to share some setup, being called in setup
, then you only need to do it in this new superclass to your test classes).
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
)
}
}
I'm not sure how to use expected: Bool
, it is a required argument for the recordFailure
method (source), but looks like Apple mostly sets it to false
.
Now you can declare your custom assert methods, like this (or as an extension on just XCTestCase
, depending on your choice):
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
)
}
}
And now it reports the error, not inside the custom assert, but at call site π.
You can also just forward the line
of, and pass to standards asserts, if you look at e.g. XCTAssertTrue
, it accepts a line argument, and a file
argument.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With