I had an idea of using a Property Wrapper to help setup Publishers and wait for their outputs in unit tests. I’m using the reference enclosing self
unofficial feature to do it. (I’m aware that it might change, but I’m trying to warm up to this use case anyway)
Here’s the code and the errors I’m getting when I try to initialize the PW variable:
@propertyWrapper
struct Await<Value> {
public static subscript<EnclosingSelf: XCTestCase>(
_enclosingInstance instance: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value?>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
) -> Value? {
// Setup expectation, fulfilled when the publisher emits a value.
let expectation = instance.expectation(description: "Waiting for publisher")
// Asserting no failure for now, ideally this wouldn't matter as we can change the `value` to be of type Result<Value, Error>? and handle failures.
let cancellable = instance[keyPath: storageKeyPath].publisher
.assertNoFailure()
.sink(receiveValue: { value in
instance[keyPath: storageKeyPath].value = value
expectation.fulfill()
})
instance.waitForExpectations(timeout: 3)
cancellable.cancel()
// Ideally I would use XCTUnwrap and throw here so the caller doesn't have to.
return instance[keyPath: storageKeyPath].value
}
@available(*, unavailable)
var wrappedValue: Value? {
get { fatalError() }
set { fatalError() }
}
private var publisher: AnyPublisher<Value, Never>
private var value: Value?
public init(_ publisher: AnyPublisher<Value, Never>) {
self.publisher = publisher
}
}
And using it:
class SamplePWTests: XCTestCase {
// Error: Extraneous argument label 'wrappedValue:' in call
// suggested fix: Insert '' (does nothing)
@Await var publisher = Just(false).eraseToAnyPublisher()
}
or
func test() throws {
@Await(Just(false).eraseToAnyPublisher()) var publisher // 'wrappedValue' is unavailable
let unwrapped = try XCTUnwrap(publisher)
XCTAssertFalse(unwrapped)
}
or
class SamplePWTests: XCTestCase {
// Illegal instruction 4
@Await(Just(false).eraseToAnyPublisher()) var publisher
}
Am I missing something on why I can't initialize it with one of these options?