0Pricing
Swift Academy · Leçon

Composer et tester des fonctionnalités

Combinez les réducteurs et testez tous les cas.

Composer et tester des fonctionnalités est une leçon Swift Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Swift Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Swift Academy comprend 4 leçons au total.

Composer des fonctionnalités

Les applications réelles sont constituées de nombreuses petites fonctionnalités. TCA permet à une fonctionnalité parente d’intégrer des fonctionnalités enfants afin que chacune reste ciblée, puis de les combiner pour former un ensemble plus vaste. Les outils utilisés sont Scope et l’intégration de l’état et des actions enfants.

La composition est ce qui donne son nom à l’Architecture composable.

import ComposableArchitecture

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

Intégrer l’état enfant

Le State du parent contient l’état de l’enfant sous la forme d’une propriété. L’énumération Action du parent possède un cas qui enveloppe les actions de l’enfant. Cette imbrication reflète celle de l’interface utilisateur.

Ici, un parent contient un compteur enfant.

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

Définir la portée de l’enfant

Scope exécute le réducteur enfant sur la partie de l’état et des actions du parent qui lui appartient. Vous lui fournissez un chemin de clé d’état et un chemin vers le cas d’action, puis le réducteur enfant.

Cela intègre l’enfant à la boucle de mise à jour du parent.

import ComposableArchitecture

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

Combiner plusieurs réducteurs

Le body d’une fonctionnalité peut répertorier plusieurs réducteurs ; TCA les exécute dans l’ordre pour chaque action. Vous pouvez ainsi placer un ou plusieurs Scope à côté du Reduce propre au parent.

Ici, deux enfants et la logique du parent coexistent.

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

Définir la portée du magasin pour les vues enfants

Du côté de la vue, une vue enfant a besoin d’un magasin spécialisé pour l’enfant. Le parent le dérive avec store.scope, en établissant une correspondance avec l’état et les actions de l’enfant.

Ici, la vue parente transmet un magasin dont la portée est limitée à la vue du compteur.

import ComposableArchitecture
import SwiftUI

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

Découvrir TestStore

TestStore vous permet de vérifier exactement le comportement d’une fonctionnalité. Vous envoyez des actions et décrivez les changements attendus de l’état ; si le comportement réel diffère, le test échoue. Cette vérification exhaustive permet de détecter les régressions subtiles.

Vous le créez comme un magasin normal, avec l’état initial et le réducteur.

import ComposableArchitecture
import XCTest

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

Vérifier les changements d’état

Lorsque vous envoyez une action à un TestStore, vous transmettez une fermeture qui décrit la modification attendue. Le test réussit uniquement si l’état change exactement de cette manière.

Ici, l’incrémentation doit faire passer le compteur de 0 à 1.

import ComposableArchitecture
import XCTest

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

Exhaustivité

Par défaut, un TestStore est exhaustif : vous devez prendre en compte chaque changement d’état et chaque action renvoyée par un effet. Les changements non traités font échouer le test, ce qui garantit la fiabilité de vos assertions.

Vous pouvez assouplir ce comportement avec store.exhaustivity = .off pour des tests ciblés.

import ComposableArchitecture

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

Recevoir les actions des effets

Lorsqu’un effet renvoie une action, le test doit la receive et vérifier l’état obtenu. Cela prouve que le flux asynchrone fonctionne de bout en bout.

Ici, un rechargement déclenche une réponse que le test attend, puis vérifie.

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

Remplacer les dépendances dans les tests

Pour rendre les effets déterministes, remplacez les dépendances lors de la création du TestStore. Fournissez un faux client ou une horloge de test afin que les résultats soient prévisibles.

Ici, le client de nombres est remplacé par un client qui renvoie toujours 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 }
}

Tester des fonctionnalités composées

Comme la composition imbrique l’état et les actions, vous testez un parent en lui envoyant des actions enfants enveloppées dans le cas du parent. Les mêmes assertions exhaustives s’appliquent et vérifient l’ensemble de l’arbre.

Ici, un parent reçoit une incrémentation du compteur par l’intermédiaire de sa portée.

import ComposableArchitecture
import XCTest

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

Vérification rapide : comportement de TestStore

Rappelez-vous ce qu’un TestStore par défaut exige de vos assertions.

Récapitulatif : composition et tests

Vous avez terminé de découvrir la boîte à outils TCA :

  • Scope exécute un réducteur enfant sur une partie de l’état et des actions du parent, et plusieurs réducteurs peuvent être combinés dans un même body.
  • store.scope fournit à une vue enfant son propre magasin spécialisé.
  • TestStore vérifie les changements d’état exacts lorsque vous envoyez des actions avec send.
  • Il est exhaustif par défaut et utilise receive pour les actions déclenchées par des effets.
  • Remplacez les dépendances avec withDependencies pour obtenir des tests déterministes.

Vous pouvez désormais créer, composer et tester rigoureusement des fonctionnalités avec l’Architecture composable.

import ComposableArchitecture

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

Questions Fréquemment Posées

La leçon « Composer et tester des fonctionnalités » est-elle gratuite ?

Oui — le texte complet de « Composer et tester des fonctionnalités » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Swift Academy, passe à CoddyKit PRO. Le cours Swift Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Composer et tester des fonctionnalités » ?

Combinez les réducteurs et testez tous les cas. Tu pratiques Swift Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Swift Academy ?

Aucune expérience préalable n'est requise. Swift Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Composer et tester des fonctionnalités » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Swift Academy ?

Oui. Chaque leçon Swift Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. État, action et réducteur
  2. Le Store et l’intégration à SwiftUI
  3. Effets et dépendances
  4. Composer et tester des fonctionnalités
← Retour à Swift Academy