Swift Academy · Lektion

Komposition og testning af features

Kombinér reducers, og test udtømmende.

Lektion 4 af 413 trin

Komposition og testning af features er en gratis Swift Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Swift Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Swift Academy-kurset indeholder 4 lektioner i alt.

Sammensætning af funktioner

Rigtige apps er bygget af mange små funktioner. TCA lader en overordnet funktion indeholde underfunktioner, så hver funktion kan fokusere på sit eget område, hvorefter de kan samles til en større helhed. Værktøjerne til dette er Scope samt indlejring af underfunktioners tilstand og handlinger.

Det er sammensætningen, der har givet The Composable Architecture sit navn.

import ComposableArchitecture

// A parent feature embeds child State and Action,
// then uses Scope to run the child's reducer.

Indlejring af underfunktionens tilstand

Den overordnede funktions State indeholder underfunktionens tilstand som en egenskab. Den overordnede funktions Action-enum har en case, der omslutter underfunktionens handlinger. Denne indlejring afspejler, hvordan brugergrænsefladen er indlejret.

Her indeholder en overordnet funktion en tæller som underfunktion.

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() }
}

Afgrænsning til underfunktionen

Scope kører underfunktionens reducer på den del af den overordnede tilstand og handling, der hører til underfunktionen. Du angiver en key path til tilstanden og en case path til handlingen og derefter underfunktionens reducer.

Det kobler underfunktionen ind i den overordnede funktions opdateringssløjfe.

import ComposableArchitecture

var body: some ReducerOf<Self> {
    Scope(state: \.counter, action: \.counter) {
        CounterFeature()
    }
    Reduce { state, action in
        // parent-specific logic here
        return .none
    }
}

Kombination af flere reducere

En funktions body kan indeholde flere reducere; TCA kører dem i rækkefølge for hver handling. Det lader dig placere en eller flere Scope ved siden af den overordnede funktions egen Reduce.

Her fungerer to underfunktioner sammen med logikken i den overordnede funktion.

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
    }
}

Afgrænsning af storet til underordnede visninger

På visningssiden skal en underordnet visning have en store, der er specialiseret til underfunktionen. Den overordnede visning afleder den med store.scope ved at kortlægge til underfunktionens tilstand og handling.

Her giver den overordnede visning en afgrænset store videre til tællervisningen.

import ComposableArchitecture
import SwiftUI

struct AppView: View {
    let store: StoreOf<AppFeature>
    var body: some View {
        CounterView(
            store: store.scope(state: \.counter, action: \.counter)
        )
    }
}

Introduktion til TestStore

TestStore lader dig kontrollere præcis, hvordan en funktion opfører sig. Du sender handlinger og beskriver, hvordan tilstanden skal ændre sig; hvis virkeligheden afviger, fejler testen. Denne udtømmende kontrol fanger subtile regressioner.

Du opretter den som en normal store med den indledende tilstand og reduceren.

import ComposableArchitecture
import XCTest

func testIncrement() async {
    let store = await TestStore(initialState: CounterFeature.State()) {
        CounterFeature()
    }
}

Kontrol af tilstandsændringer

Når du sender en handling til en TestStore, giver du en closure, der beskriver den forventede ændring. Testen består kun, hvis tilstanden ændrer sig præcis på denne måde.

Her skal en forøgelse hæve count fra 0 til 1.

import ComposableArchitecture
import XCTest

func testIncrement() async {
    let store = await TestStore(initialState: CounterFeature.State()) {
        CounterFeature()
    }
    await store.send(.incrementTapped) {
        $0.count = 1
    }
}

Udtømmende kontrol

Som standard er en TestStore udtømmende: du skal tage højde for alle tilstandsændringer og alle handlinger, som en effekt sender tilbage. Ubehandlede ændringer får testen til at fejle, så dine kontroller forbliver retvisende.

Du kan lempe dette med store.exhaustivity = .off i fokuserede test.

import ComposableArchitecture

// Exhaustive (default): every change must be asserted.
// Relax when you only care about part of the behavior:
store.exhaustivity = .off

Modtagelse af effekthandlinger

Når en effekt sender en handling tilbage, skal testen receive den og kontrollere den resulterende tilstand. Det beviser, at det asynkrone forløb fungerer fra start til slut.

Her udløser en genindlæsning et svar, som testen afventer og kontrollerer.

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
    }
}

Tilsidesættelse af afhængigheder i test

For at gøre effekter deterministiske tilsidesætter du afhængigheder, når du opretter TestStore. Angiv en falsk klient eller et testur, så resultaterne bliver forudsigelige.

Her erstattes tal-klienten med en, der altid returnerer 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 }
}

Test af sammensatte funktioner

Fordi sammensætning indlejrer tilstand og handlinger, tester du en overordnet funktion ved at sende underfunktionens handlinger pakket ind i den overordnede case. De samme udtømmende kontroller gælder og bekræfter hele træet.

Her modtager en overordnet funktion en tællerforøgelse gennem sit 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
    }
}

Hurtigt tjek: TestStore-adfærd

Husk, hvad en TestStore som standard kræver af dine kontroller.

Opsamling: sammensætning og test

Du har gennemført TCA-værktøjskassen:

  • Scope kører en underfunktions reducer på en del af den overordnede tilstand og handling, og flere reducere kan kombineres i én body.
  • store.scope giver en underordnet visning sin egen specialiserede store.
  • TestStore kontrollerer præcise tilstandsændringer, når du send-er handlinger.
  • Den er som standard udtømmende og bruger receive til effektdrevne handlinger.
  • Tilsidesæt afhængigheder med withDependencies for at få deterministiske test.

Du kan nu bygge, sammensætte og grundigt teste funktioner med The Composable Architecture.

import ComposableArchitecture

// Recap: Scope to compose, TestStore to verify.
// Exhaustive assertions + injected fakes = confidence.
Gratis at komme i gang

Lær Swift med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
122
Lektioner
409

Ofte stillede spørgsmål

Er lektionen “Komposition og testning af features” gratis?

Ja — alle 3 lektioner i læringssporet Swift Academy, inklusive “Komposition og testning af features”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Swift Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Komposition og testning af features”?

Kombinér reducers, og test udtømmende. Du øver dig i Swift Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Swift Academy?

Der kræves ingen tidligere erfaring. Swift Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.

Hvor lang tid tager lektionen “Komposition og testning af features”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Swift Academy-lektion?

Ja. Alle Swift Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. State, action og reducer
  2. Store og SwiftUI-integration
  3. Effects og dependencies
  4. Komposition og testning af features
← Tilbage til Swift Academy