pointfreeco / swift-composable-architecture

A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
https://www.pointfree.co/collections/composable-architecture
MIT License
12.35k stars 1.44k forks source link

@Shared State in Tests for consecutive actions is modified prematurely #3306

Closed Badlazzor closed 1 month ago

Badlazzor commented 1 month ago

Description

Consider having a Reducer that has 4 actions:

enum Action {
    case fetchA
    case resultForA
    case fetchB
    case resultForB
}

It also shares the As and Bs in a @Shared [A: IdentifiedArray<B>] State.

The Reducer should do the following:

The problem I see is when testing and sending fetchA to the TestStore the actions are received as expected, but the state changes are accumulated into the first actions changes in the @Shared state.

Checklist

Expected behavior

Sending fetchA to the TestStore would result in the following:

store.receive(.resultForA) {
// only changes for As in the Shared State
}

store.receive(.fetchB)
store.receive(.resultsForB) {
// only changes for Bs in the Shared State
}

Actual behavior

Sending fetchA to the TestStore would result in the following:

store.receive(.resultForA) {
// changes (albeit correct overall) to both As and Bs under them
}

store.receive(.fetchB)
store.receive(.resultsForB) {
// no state change
}

Steps to reproduce

No response

The Composable Architecture version information

1.13.1

Destination operating system

iOS 17.5

Xcode version information

Version 15.4 (15F31d)

Swift Compiler version information

Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-darwin23.3.0
mbrandonw commented 1 month ago

Hi @Badlazzor, the behavior you are witnessing is unfortunately expected, though not necessary ideal. I believe this behavior is documented, but I couldn't find it and so I will have to look into that.

But currently the TestStore eagerly executes effect actions when they are received, which means the logic that mutates shared state is happening before one even does store.receive. Before releasing the shared state tools we looked into making TestStore delay executing an effect action until one actually does store.receive, but unfortunately we found that would be a breaking change. There are tests out there that assert on dependencies in a specific way that would be broken by delaying the execution of the effect action.

So, we do plan on fixing this issue, but it will have to wait for 2.0. Since we do not consider this an issue with the library today I am going to convert it to a discussion. Please feel free to continue the conversation over there.