0Pricing
Swift Academy · Lektion

Features kombinieren und testen

Kombinieren Sie Reducer und testen Sie vollständig.

Features kombinieren und testen ist eine kostenlose Swift Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Swift Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Swift Academy-Kurs umfasst insgesamt 4 Lektionen.

Features zusammensetzen

Reale Apps bestehen aus vielen kleinen Features. Mit TCA kann ein übergeordnetes Feature untergeordnete Features einbetten, sodass jedes fokussiert bleibt, und sie anschließend zu einem größeren Ganzen verbinden. Dafür dienen Scope sowie das Einbetten des Zustands und der Aktionen untergeordneter Features.

Die Komposition ist der Grund, warum The Composable Architecture diesen Namen trägt.

import ComposableArchitecture

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

Zustand untergeordneter Features einbetten

Der State des übergeordneten Features enthält den Zustand des untergeordneten Features als Eigenschaft. Das Action-Enum des übergeordneten Features besitzt einen Case, der die Aktionen des untergeordneten Features umschließt. Diese Verschachtelung spiegelt die Verschachtelung der Benutzeroberfläche wider.

Hier enthält ein übergeordnetes Feature ein untergeordnetes Zähler-Feature.

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

Scope auf das untergeordnete Feature anwenden

Scope führt den Reducer des untergeordneten Features für den Teil des übergeordneten Zustands und der übergeordneten Aktion aus, der zu diesem Feature gehört. Sie übergeben ihm einen KeyPath zum Zustand, einen Case-Path für die Aktion und anschließend den Reducer des untergeordneten Features.

So wird das untergeordnete Feature in den Aktualisierungszyklus des übergeordneten Features eingebunden.

import ComposableArchitecture

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

Mehrere Reducer kombinieren

Der body eines Features kann mehrere Reducer auflisten; TCA führt sie für jede Aktion in der angegebenen Reihenfolge aus. So können Sie einen oder mehrere Scopes neben dem eigenen Reduce des übergeordneten Features platzieren.

Hier bestehen zwei untergeordnete Features und die Logik des übergeordneten Features nebeneinander.

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

Den Store für untergeordnete Views zuschneiden

Auf der View-Seite benötigt eine untergeordnete View einen auf sie zugeschnittenen Store. Die übergeordnete View leitet ihn mit store.scope ab und bildet ihn auf den Zustand und die Aktionen des untergeordneten Features ab.

Hier übergibt die übergeordnete View der Zähler-View einen zugeschnittenen Store.

import ComposableArchitecture
import SwiftUI

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

TestStore einführen

Mit dem TestStore können Sie exakt prüfen, wie sich ein Feature verhält. Sie senden Aktionen und beschreiben, wie sich der Zustand ändern muss; weicht das tatsächliche Verhalten ab, schlägt der Test fehl. Diese umfassende Prüfung erkennt subtile Regressionen.

Sie erstellen ihn wie einen normalen Store mit Anfangszustand und Reducer.

import ComposableArchitecture
import XCTest

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

Zustandsänderungen prüfen

Wenn Sie eine Aktion an einen TestStore senden, übergeben Sie eine Closure, die die erwartete Änderung beschreibt. Der Test ist nur erfolgreich, wenn sich der Zustand genau entsprechend ändert.

Hier muss das Inkrementieren den Zähler von 0 auf 1 erhöhen.

import ComposableArchitecture
import XCTest

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

Exhaustivität

Standardmäßig ist ein TestStore exhaustiv: Sie müssen jede Zustandsänderung und jede Aktion berücksichtigen, die ein Effekt zurückmeldet. Nicht behandelte Änderungen führen zum Fehlschlagen des Tests und sorgen dafür, dass Ihre Assertions verlässlich bleiben.

Für fokussierte Tests können Sie diese Prüfung mit store.exhaustivity = .off lockern.

import ComposableArchitecture

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

Aktionen aus Effekten empfangen

Wenn ein Effekt eine Aktion zurücksendet, muss der Test sie mit receive empfangen und den daraus resultierenden Zustand prüfen. So wird bestätigt, dass der asynchrone Ablauf durchgängig funktioniert.

Hier löst ein Neuladen eine Antwort aus, die der Test abwartet und prüft.

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

Abhängigkeiten in Tests überschreiben

Damit Effekte deterministisch sind, überschreiben Sie die Abhängigkeiten beim Erstellen des TestStore. Stellen Sie einen Fake-Client oder eine Testuhr bereit, damit die Ergebnisse vorhersehbar sind.

Hier wird der Zahlen-Client durch einen ersetzt, der immer 42 zurückgibt.

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

Zusammengesetzte Features testen

Da Komposition Zustand und Aktionen verschachtelt, testen Sie ein übergeordnetes Feature, indem Sie in einen Case des übergeordneten Features eingeschlossene Aktionen des untergeordneten Features senden. Die gleichen exhaustiven Assertions gelten auch hier und prüfen den gesamten Baum.

Hier empfängt ein übergeordnetes Feature über seinen Scope eine Zähleraktion zum Erhöhen.

import ComposableArchitecture
import XCTest

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

Kurztest: Verhalten des TestStore

Rufen Sie sich ins Gedächtnis, was ein standardmäßiger TestStore von Ihren Assertions verlangt.

Zusammenfassung: Zusammensetzen und Testen

Sie haben das TCA-Werkzeugset vollständig kennengelernt:

  • Scope führt einen untergeordneten Reducer für einen Teil des Zustands und der Aktion des übergeordneten Features aus, und mehrere Reducer können in einem body kombiniert werden.
  • store.scope übergibt einer untergeordneten View ihren eigenen spezialisierten Store.
  • TestStore prüft exakte Zustandsänderungen, wenn Sie Aktionen mit send senden.
  • Er ist standardmäßig exhaustiv und verwendet receive für aktionsgesteuerte Effekte.
  • Mit withDependencies überschreiben Sie Abhängigkeiten für deterministische Tests.

Sie können nun mit The Composable Architecture Features erstellen, zusammensetzen und rigoros testen.

import ComposableArchitecture

// Recap: Scope to compose, TestStore to verify.
// Exhaustive assertions + injected fakes = confidence.

Häufig gestellte Fragen

Ist die Lektion „Features kombinieren und testen“ kostenlos?

Ja — der vollständige Text von „Features kombinieren und testen“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Swift Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Swift Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Features kombinieren und testen“?

Kombinieren Sie Reducer und testen Sie vollständig. Du übst Swift Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Swift Academy zu starten?

Keine Vorkenntnisse erforderlich. Swift Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Features kombinieren und testen“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Swift Academy-Lektion Code schreiben und ausführen?

Ja. Jede Swift Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. State, Action und Reducer
  2. Der Store und die SwiftUI-Integration
  3. Effects und Dependencies
  4. Features kombinieren und testen
← Zurück zu Swift Academy