I'm trying to test an API I'm writing with Structured Concurrency. I prefer to run my tests with continueAfterFailure = false
so that I may establish preconditions on my tests. At the moment my test is:
func testRssUrl() async throws {
self.continueAfterFailure = false // 1
let xml = PodcastXml.url(url)
let feed = try await xml.feed // 2
let rssFeed: RSSFeed? = feed.rssFeed
XCTAssertNil(rssFeed) // 3 Included for this example
XCTAssertNotNil(rssFeed) // 4
validate(zebraFeed: rssFeed!) // 5
}
My expectation of Swift Concurrency is that the try await
(2) should hold the method, until the expression resolves to a value or the expression throws. Either of the XCTAssertNil
(3) or XCTAssertNotNil
(4) should fail (in the actual test, I'm only using XCTAssertNotNil
).
As written, between the two asserts and the try, validate(zebraFeed:)
(5) should never be called because of continueAfterFailure
(1).
Yet it is. The test still fails because XCTAssertNil
(3) is failing, which is my actual expectation, but the test execution should also be stopping there.
What am I missing?
====Update======
Following replies I have an expanded example:
class TestAwaitTests: XCTestCase {
struct Thing {
let value = true
func doSomething() async throws -> Int? {
try await Task.sleep(nanoseconds: 5_000_000_000)
return 1
}
}
func validation(thing: Thing, file: StaticString = #file, line: UInt = #line) {
XCTAssertFalse(thing.value)
XCTAssertTrue(thing.value) // Breakpoint here
}
func testTest() async throws {
continueAfterFailure = false
let thing = Thing()
let result = try await thing.doSomething()
print("print 1")
XCTAssertNil(result)
print("print 2")
XCTAssertNotNil(result)
print("print 3")
validation(thing: thing)
enum Err: Error { case oops }
throw Err.oops
print("print 4")
}
}
With console output:
print 1
print 2
print 3
The test fails, as expected. What is unexpected is that the statements print 2
and print 3
are produced. continueAfterFailure
should be stopping the test first assertion.
===Update 2===
The second case had been updated with a validate(thing:)
method. With continueAfterFailure
set to false, the line marked Breakpoint here
should never execute.