Why .dependency(\.date)
is evaluated only once?
#2122
-
Hey! I'm not sure if this is a TCA/Dependency bug so I wanted to ask my question here first. I want to mock The only way to make it work is to ditch Why is that? This is NOT working import XCTest
import ComposableArchitecture
import Dependencies
struct DateReducer: ReducerProtocol {
struct State: Equatable {
var displayDate: String
}
enum Action: Equatable {
case updateDate
}
@Dependency(\.date.now) var now
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action {
case .updateDate:
state.displayDate = "Timestamp: \(Int(now.timeIntervalSince1970))"
return .none
}
}
}
final class DateMockingTests: XCTestCase {
func test_changingDateOnRuntime() {
let store = TestStore(
initialState: DateReducer.State(displayDate: "")) {
DateReducer()
.dependency(\.date, DateGenerator { Date(timeIntervalSince1970: 100) })
}
store.send(.updateDate) {
$0.displayDate = "Timestamp: 100"
}
store.dependencies.date = DateGenerator { Date(timeIntervalSince1970: 200) }
store.send(.updateDate) {
$0.displayDate = "Timestamp: 200" // Fail: value is "Timestamp: 100"
}
store.dependencies.date = DateGenerator { Date(timeIntervalSince1970: 300) }
store.send(.updateDate) {
$0.displayDate = "Timestamp: 300" // Fail: value is "Timestamp: 100"
}
}
} This IS working import XCTest
import ComposableArchitecture
import Dependencies
struct DateReducer: ReducerProtocol {
struct State: Equatable {
var displayDate: String
}
enum Action: Equatable {
case updateDate
}
@Dependency(\.date.now) var now
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action {
case .updateDate:
state.displayDate = "Timestamp: \(Int(now.timeIntervalSince1970))"
return .none
}
}
}
final class DateMockingTests: XCTestCase {
func test_changingDateOnRuntime() {
let store = TestStore(
initialState: DateReducer.State(displayDate: "")) {
DateReducer()
}
store.dependencies.date = DateGenerator { Date(timeIntervalSince1970: 100) }
store.send(.updateDate) {
$0.displayDate = "Timestamp: 100"
}
store.dependencies.date = DateGenerator { Date(timeIntervalSince1970: 200) }
store.send(.updateDate) {
$0.displayDate = "Timestamp: 200"
}
store.dependencies.date = DateGenerator { Date(timeIntervalSince1970: 300) }
store.send(.updateDate) {
$0.displayDate = "Timestamp: 300"
}
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
There might be a couple things going on, but right off the bat I think one problem is that the Instead, I think you want to do something like this: @MainActor
final class DateMockingTests: XCTestCase {
func test_changingDateOnRuntime() async {
let store = TestStore(
initialState: DateReducer.State(displayDate: ""),
reducer: { DateReducer() },
withDependencies: { $0.date.now = Date(timeIntervalSince1970: 100) }
)
await store.send(.updateDate) {
$0.displayDate = "Timestamp: 100" // ✅
}
store.dependencies.date.now = Date(timeIntervalSince1970: 200)
await store.send(.updateDate) {
$0.displayDate = "Timestamp: 200" // ✅
}
store.dependencies.date.now = Date(timeIntervalSince1970: 300)
await store.send(.updateDate) {
$0.displayDate = "Timestamp: 300" // ✅
}
}
} That correctly sets the dependencies the TestStore will use. |
Beta Was this translation helpful? Give feedback.
There might be a couple things going on, but right off the bat I think one problem is that the
.dependencies
extension on reducers is not the right tool to set up dependencies in tests,because it's immediately going to be overridden by the default test dependencies(edit: see below answer). It is for modifying dependencies in the reducer tree from parent to child in application code.Instead, I think you want to do something like this: