Post

Replies

Boosts

Views

Activity

Await expectations with Swift Testing?
I'm used to wait for expectations when using XCTest, but I'm completely stumped by the seemingly-absent option for expecting some parts of my code to be called in Swift Testing 🤔 Consider the following: protocol MyAPI { func getData() async throws -> String } class MockAPI: MyAPI { let stub: () async throws -> String init(stub: @escaping () async throws -> String = { "hello" }) { self.stub = stub } func getData() async throws -> String { try await stub() } } Which is being used in my view model: class MyViewModel: ObservableObject { private let api: MyAPI @Published var data: String init(api: MyAPI) { self.api = api } func refresh() async throws { self.data = try await api.getData() } } When I wrote tests in the past, I would simply override the stub of the MockAPI implementation and fulfill an expectation in there: class MyViewModelTests: XCTestCase { func testModelCallsAPIOnRefresh() async throws { let expectCallsAPI = expectation("Model should call API") let api = MockAPI { expectCallsAPI.fulfill() return "hello" } let model = MyViewModel(api: api) try await model.refresh() await fulfillment(of: [expectCallsAPI], timeout: 1) } } How would I go about checking that my model does indeed call the API when using Swift Testing?
1
0
682
Aug ’24
KeyedDecodingContainerProtocol - Why so many requirements?
The interface for the KeyedDecodingContainer protocol declares a bunch of requirements like such: func decode(_ type: Bool.Type, forKey key: Self.Key) throws -> Bool func decode(_ type: String.Type, forKey key: Self.Key) throws -> String func decode(_ type: Double.Type, forKey key: Self.Key) throws -> Double // etc. As well as a generic one: func decode<T>(_ type: T.Type, forKey key: Self.Key) throws -> T where T : Decodable Is that done for performance reasons? I have implemented a return-type-aware generic extension on KeyedDecodingContainer which always defers to the aforementioned generic implementation: func decode<Value: Decodable>( type: Value.Type = Value.self, _ key: Key ) throws -> Value { try decode(type, forKey: key) } Which allows me to decode anything in the following way: self.myValue = container.decode(.myValue) // instead of self.myValue = container.decode(String.self, forKey: .myValue) Now, this works fine and all, but upon submitting this extension for code review, one of my colleague raised concerns for defaulting to calling only the generic implementation rather than the statically-typed one (where applicable). So, my question is, are these concerns valid?
1
0
375
Mar ’24