Replies: 3 comments
-
Hey @ahbou!
Indeed, but in your case, you're reusing the await DependencyValues.withValues {
$0.apiClient = mockClient
} operation: {
@Dependency(\.apiClient) var apiClient
let result = apiClient.items()
XCTAssertEqual(result.count, 5)
} As your code doesn't seem to be async, you can probably drop the The way you're defining |
Beta Was this translation helpful? Give feedback.
-
If you plan to use In particular, you should reset all of extension DependencyValues {
static func withTesting<R>(
_ updateValuesForOperation: (inout Self) throws -> Void,
operation: () throws -> R
) rethrows -> R {
try Self.withValues {
$0 = DependencyValues()
$0.context = .test
try updateValuesForOperation(&$0)
} operation: {
try operation()
}
}
static func withTesting<R>(
_ updateValuesForOperation: (inout Self) async throws -> Void,
operation: () async throws -> R
) async rethrows -> R {
try await Self.withValues {
$0 = DependencyValues()
$0.context = .test
try await updateValuesForOperation(&$0)
} operation: {
try await operation()
}
}
} With those helpers it will allow you to write tests in the following manner: DependencyValues.withTesting {
$0.apiClient = .mock
} operation: {
let model = FeatureModel()
model.buttonTapped()
XCTAssertEqual(model.data, [...])
} The code snippets you shared aren't quite how one should test with dependencies. First, as @tgrapperon, you shouldn't reach into Second, you shouldn't need to access dependencies directly inside the And third, looking at the public extension UserClient {
static let live = UserClient(
currentUser: { id in
@Dependency(\.apiClient) var apiClient;
return await apiClient.getUser(id: id)
}
)
} |
Beta Was this translation helpful? Give feedback.
-
Thank you both for your answers. It wasn't obvious that I think I cleaned up the sample code too much, here's a more accurate description of what I'm testing: I have an For each test I recreate func testFetchingItems() async {
let scheduler = DispatchQueue.test
let items = Future<[Item], Never> { callback in
scheduler.schedule(after: scheduler.now.advanced(by: 1)) { callback(.success(Item.mocks)) }
}.eraseToAnyPublisher()
let mockClient = ApiClient(
items: {
items
},
getUser: XCTUnimplemented("\(Self.self).getUser")
)
await DependencyValues.withTesting(context: .live, {
$0.apiClient = mockClient
}, operation: {
@Dependency(\.itemsClient) var itemsClient
var result = [Item]()
let task = Task {
do {
for try await items in itemsClient.items() {
result = items
}
} catch {}
}
XCTAssertEqual(result, [])
await scheduler.advance(by: 1)
XCTAssertEqual(result.count, 4)
task.cancel()
})
} Resetting dependencies for each test is exactly what I was missing. |
Beta Was this translation helpful? Give feedback.
-
I was reading the #1287 thread about composing dependencies. I'm using them outside TCA.
In my tests I use DependencyValues.withValue to override a couple of dependencies this way:
I have 3 tests and for each test a different mockClient.
If I run them together they fail randomly because they use the wrong mockClient provided inside the operation block of DependencyValues.withValues.
I'm not sure if my logic is flawed or if it's a bug. My understanding of withValues is that every call inside the operationblock will use the overridden values.
Is it safe to have the assertions within DependencyValues.withValues?
I'm doing all this because in the live implementation, I'm accessing dependencies this way
Is
DependencyValues._current.apiClient
abusing the Dependencies system or is it ok to use it like that?Beta Was this translation helpful? Give feedback.
All reactions