If I understand correctly, the concern is due to the fact that call site does not let the caller know that the call passed to it will be lazily called.
It has been my experience that @autoclosure
should be used in places where the caller would expect the call to be lazy. The fact that it is lazy is almost an implementation detail. (IMO)
@autoclosure
helps keep the call site looking nice and the code performs as expected. (IMO)
I have written some test helpers and have used @autoclosure
so that the API matches XCTest asserts' API. The call point feels natural and the values are lazily evaluated as expected.
I would chose option #1 and keep @autoclosure
as-is. My next choice would be #2 and remove @autoclosure
completely. The other options are thoughtful, but don't seem to add value to the situations where I would use @autoclosure
today.
Here is an example test helper that is using @autoclosure
.
func XCTCaptureError<T>(
from block: @autoclosure () async throws -> T,
_ message: @autoclosure () -> String = "",
file: StaticString = #file,
line: UInt = #line
) async -> Error? {
...
}
And here is an example call site usage:
let capturedError = await XCTCaptureError(from: try await sut.createPoem())
without @autoclosure
I guess the call site would be:
let capturedError = await XCTCaptureError(from: { try await sut.createPoem() })
or:
let capturedError = await XCTCaptureError { try await sut.createPoem() }
I may be biased, but I like the @autoclosure
version. It seems to be more clear at the call point.
here is the full function and usage for completeness:
func XCTCaptureError<T>(
from block: @autoclosure () async throws -> T,
_ message: @autoclosure () -> String = "",
file: StaticString = #file,
line: UInt = #line
) async -> Error? {
do {
_ = try await block()
let description = ["XCTCaptureError failed: should have thrown an error", message()]
.filter { !$0.isEmpty }
.joined(separator: " - ")
let context: XCTSourceCodeContext = .init(location: .init(filePath: file, lineNumber: line))
record(.init(type: .assertionFailure, compactDescription: description, sourceCodeContext: context))
return nil
} catch {
return error
}
}
func test_failingGetUsername_createPoem_fails() async throws {
let error = XCTAnyError()
let (sut, _) = makeSUT(getUsernameResult: .failure(error))
let capturedError = await XCTCaptureError(from: try await sut.createPoem())
XCTAssertCastEqual(capturedError, error)
}
Repo link: TDDKit