Composizione e test delle funzionalità
Combini i reducer e testi ogni caso in modo esaustivo.
Composizione e test delle funzionalità è una lezione Swift Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Swift Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Swift Academy include 4 lezioni in totale.
Comporre le funzionalità
Le app reali sono costruite a partire da molte piccole funzionalità. TCA consente a una funzionalità genitore di incorporare funzionalità figlie, così ognuna rimane focalizzata, per poi combinarle in un insieme più ampio. Gli strumenti per farlo sono Scope e l'inclusione dello stato e delle action figlie.
È la composizione a dare il nome The Composable Architecture.
import ComposableArchitecture
// A parent feature embeds child State and Action,
// then uses Scope to run the child's reducer.Incorporare lo stato figlio
Lo State del genitore contiene lo stato del figlio come proprietà. L'enum Action del genitore ha un case che racchiude le action del figlio. Questa struttura annidata rispecchia il modo in cui è annidata l'interfaccia.
In questo caso, un genitore contiene un figlio contatore.
import ComposableArchitecture
@Reducer
struct AppFeature {
@ObservableState
struct State: Equatable {
var counter = CounterFeature.State()
}
enum Action {
case counter(CounterFeature.Action)
}
var body: some ReducerOf<Self> { EmptyReducer() }
}Definire lo scope per il figlio
Scope esegue il reducer figlio sulla porzione dello stato e dell'action del genitore che gli appartiene. Gli vengono forniti un key path dello stato e un case path dell'action, seguiti dal reducer figlio.
In questo modo il figlio viene integrato nel ciclo di aggiornamento del genitore.
import ComposableArchitecture
var body: some ReducerOf<Self> {
Scope(state: \.counter, action: \.counter) {
CounterFeature()
}
Reduce { state, action in
// parent-specific logic here
return .none
}
}Combinare più reducer
Il body di una funzionalità può elencare diversi reducer; TCA li esegue nell'ordine per ogni action. In questo modo è possibile affiancare uno o più Scope al Reduce del genitore.
In questo caso convivono due figli e la logica del genitore.
import ComposableArchitecture
var body: some ReducerOf<Self> {
Scope(state: \.profile, action: \.profile) { ProfileFeature() }
Scope(state: \.settings, action: \.settings) { SettingsFeature() }
Reduce { state, action in
return .none
}
}Definire lo scope dello store per le view figlie
Dal lato della view, una view figlia ha bisogno di uno store specializzato per il figlio. Il genitore lo ottiene con store.scope, eseguendo il mapping verso lo stato e l'action del figlio.
In questo caso, la view genitore passa uno store con scope alla view del contatore.
import ComposableArchitecture
import SwiftUI
struct AppView: View {
let store: StoreOf<AppFeature>
var body: some View {
CounterView(
store: store.scope(state: \.counter, action: \.counter)
)
}
}Presentazione di TestStore
TestStore consente di verificare esattamente il comportamento di una funzionalità. Si inviano action e si descrive come deve cambiare lo stato; se il comportamento effettivo è diverso, il test fallisce. Questa verifica esaustiva permette di individuare regressioni non evidenti.
Lo si crea come un normale store, specificando lo stato iniziale e il reducer.
import ComposableArchitecture
import XCTest
func testIncrement() async {
let store = await TestStore(initialState: CounterFeature.State()) {
CounterFeature()
}
}Verificare le modifiche allo stato
Quando si invia un'action a un TestStore, si passa una closure che descrive la modifica attesa. Il test ha esito positivo solo se lo stato cambia esattamente in quel modo.
In questo caso, l'incremento deve portare il count da 0 a 1.
import ComposableArchitecture
import XCTest
func testIncrement() async {
let store = await TestStore(initialState: CounterFeature.State()) {
CounterFeature()
}
await store.send(.incrementTapped) {
$0.count = 1
}
}Completezza
Per impostazione predefinita, un TestStore è esaustivo: è necessario tenere conto di ogni modifica dello stato e di ogni action restituita da un effetto. Le modifiche non gestite fanno fallire il test, mantenendo le asserzioni rigorose.
Per test mirati, è possibile disattivare questa impostazione con store.exhaustivity = .off.
import ComposableArchitecture
// Exhaustive (default): every change must be asserted.
// Relax when you only care about part of the behavior:
store.exhaustivity = .offRicevere le action degli effetti
Quando un effetto invia un'action, il test deve eseguire receive e verificare lo stato risultante. In questo modo si dimostra che il flusso asincrono funziona dall'inizio alla fine.
In questo caso, un reload attiva una risposta che il test attende e verifica.
import ComposableArchitecture
import XCTest
func testReload() async {
let store = await TestStore(initialState: NumberFeature.State()) {
NumberFeature()
}
await store.send(.reload)
await store.receive(\.response) {
$0.value = 42
}
}Sovrascrivere le dipendenze nei test
Per rendere deterministici gli effetti, sovrascriva le dipendenze quando crea il TestStore. Fornisca un client fittizio o un orologio di test, così i risultati saranno prevedibili.
In questo caso, il client dei numeri viene sostituito con uno che restituisce sempre 42.
import ComposableArchitecture
import XCTest
func testReload() async {
let store = await TestStore(initialState: NumberFeature.State()) {
NumberFeature()
} withDependencies: {
$0.numberClient.fetch = { 42 }
}
await store.send(.reload)
await store.receive(\.response) { $0.value = 42 }
}Testare le funzionalità composte
Poiché la composizione annida stato e action, per testare un genitore si inviano action del figlio racchiuse nel case del genitore. Le stesse asserzioni esaustive rimangono valide e verificano l'intero albero.
In questo caso, un genitore riceve un incremento del contatore attraverso il proprio scope.
import ComposableArchitecture
import XCTest
func testChildInParent() async {
let store = await TestStore(initialState: AppFeature.State()) {
AppFeature()
}
await store.send(.counter(.incrementTapped)) {
$0.counter.count = 1
}
}Verifica rapida: comportamento di TestStore
Ricordi cosa richiede alle Sue asserzioni un TestStore predefinito.
Riepilogo: composizione e test
Ha completato il toolkit di TCA:
Scopeesegue un reducer figlio su una porzione dello stato e dell'action del genitore, e diversi reducer possono essere combinati in un unicobody.store.scopefornisce a una view figlia il proprio store specializzato.TestStoreverifica le modifiche esatte dello stato quando si inviano action consend.- È esaustivo per impostazione predefinita e usa
receiveper le action generate dagli effetti. - Le dipendenze possono essere sovrascritte con
withDependenciesper ottenere test deterministici.
Ora può creare, comporre e testare rigorosamente le funzionalità con The Composable Architecture.
import ComposableArchitecture
// Recap: Scope to compose, TestStore to verify.
// Exhaustive assertions + injected fakes = confidence.Domande Frequenti
La lezione «Composizione e test delle funzionalità» è gratuita?
Sì — il testo completo di «Composizione e test delle funzionalità» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Swift Academy, passa a CoddyKit PRO. Il corso Swift Academy include 4 lezioni in totale.
Cosa imparerò in «Composizione e test delle funzionalità»?
Combini i reducer e testi ogni caso in modo esaustivo. Eserciti Swift Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Swift Academy?
Non è richiesta alcuna esperienza precedente. Swift Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Composizione e test delle funzionalità»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Swift Academy?
Sì. Ogni lezione Swift Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Stato, azione e reducer
- Lo Store e l’integrazione con SwiftUI
- Effetti e dipendenze
- Composizione e test delle funzionalità