How to test long running effect that sends actions?

Hi I'm new to the library and trying to understand things. I have an action that returns an Effect which sends a "decrement time" action every second. I want to test that every second, I get the decrement time action. I must writing the test incorrectly, because I'm receiving no actions. Can someone help me understand what's happening?

Here's my feature:

struct MyFeature: ReducerProtocol {
    private enum CancelID { }
    struct State: Equatable {

    }
    
    enum Action: Equatable {
        case start
        case pause
        case decrementTime
    }

    @Dependency(\.continuousClock) var clock

    func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
        switch action {
        case .start:
            return .run { send in
                while !Task.isCancelled {
                    try await clock.sleep(for: .seconds(1))
                    await send.send(.decrementTime)
                }
            }.cancellable(id: CancelID, cancelInFlight: true)
        case .pause:
            return Effect.cancel(id: TimerCancelID.self)
        case .decrementTime:
            return .none
        }
    }
}

And here is my test:

@MainActor
final class MyFeatureTests: XCTestCase {

    func test_starting_timer_decrements_time_every_second() async {
        let testClock = TestClock()
        let store = TestStore(initialState: MyFeature.State(), reducer: MyFeature())
        store.dependencies.continuousClock = TestClock()

        await store.send(.start)
        await testClock.advance(by: .seconds(1))
        await store.receive(.decrementTime)
        await testClock.advance(by: .seconds(1))
        await store.receive(.decrementTime)
        await testClock.advance(by: .seconds(1))
        await store.receive(.decrementTime)
        await store.send(.pause)

        await testClock.run()
    }
}

I'm getting an error on each receive that an action was expected, but received none. Thanks in advance for the help!

The problem is in these lines:

let testClock = TestClock()
…
store.dependencies.continuousClock = TestClock()

You are creating a brand new test clock when assigning the dependency rather than using the one you created above. If you do this instead it should work:

store.dependencies.continuousClock = testClock
1 Like

Wow what a silly mistake! Thank you!