Swift Academy · Aula

Composição e Teste de Funcionalidades

Combine redutores e teste exaustivamente.

Aula 4 de 413 etapas

Composição e Teste de Funcionalidades é uma aula grátis de Swift Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Swift Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Swift Academy inclui 4 aulas no total.

Composição de funcionalidades

Aplicativos reais são construídos a partir de muitas funcionalidades pequenas. O TCA permite que uma funcionalidade pai incorpore funcionalidades filhas para que cada uma permaneça focada e, em seguida, as combine em um todo maior. As ferramentas para isso são Scope e a incorporação do state e das ações filhas.

É a composição que dá à Arquitetura Componível seu nome.

import ComposableArchitecture

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

Incorporando o state da funcionalidade filha

O State da funcionalidade pai contém o state da filha como uma propriedade. A enumeração Action da funcionalidade pai tem um caso que encapsula as ações da filha. Esse aninhamento reflete a forma como a interface se aninha.

Aqui, uma funcionalidade pai contém uma filha counter.

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

Definindo o escopo da filha

Scope executa o redutor filho na parte do state e da ação pai que lhe pertence. Você fornece um caminho de chave de state e um caminho de caso de ação e, em seguida, o redutor filho.

Isso integra a filha ao ciclo de update do pai.

import ComposableArchitecture

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

Combinando vários redutores

O body de uma funcionalidade pode listar vários redutores; o TCA os executa em ordem para cada ação. Isso permite colocar um ou mais Scopes ao lado do próprio Reduce da funcionalidade pai.

Aqui, duas filhas e a lógica do pai coexistem.

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

Configurando o store para visualizações filhas

No lado da visualização, uma visualização filha precisa de um store especializado para a filha. A visualização pai o deriva com store.scope, mapeando para o state e a ação da filha.

Aqui, a visualização pai passa um store com scope para a visualização counter.

import ComposableArchitecture
import SwiftUI

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

Apresentando o TestStore

O TestStore permite verificar exatamente como uma funcionalidade se comporta. Você envia ações e descreve como o state deve mudar; se a realidade for diferente, o teste falha. Essa verificação exaustiva detecta regressões sutis.

Você o cria como um store normal, com o state inicial e o redutor.

import ComposableArchitecture
import XCTest

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

Verificando alterações de state

Quando você envia uma ação para um TestStore, passa um fechamento que descreve a mutação esperada. O teste é aprovado somente se o state mudar exatamente dessa forma.

Aqui, incrementar deve elevar a contagem de 0 para 1.

import ComposableArchitecture
import XCTest

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

Exaustividade

Por padrão, um TestStore é exaustivo: você precisa considerar cada alteração de state e cada ação que um efeito devolve. Alterações não tratadas fazem o teste falhar, mantendo suas afirmações honestas.

Você pode flexibilizar isso com store.exhaustivity = .off para testes focados.

import ComposableArchitecture

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

Recebendo ações de efeitos

Quando um efeito envia uma ação de volta, o teste precisa usar receive para recebê-la e verificar o state resultante. Isso comprova que o fluxo assíncrono funciona de ponta a ponta.

Aqui, um recarregamento dispara uma resposta que o teste aguarda 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
    }
}

Substituindo dependências nos testes

Para tornar os efeitos determinísticos, substitua as dependências ao construir o TestStore. Forneça um cliente falso ou um relógio de teste para que os resultados sejam previsíveis.

Aqui, o cliente numérico é substituído por um que sempre retorna 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 }
}

Testando funcionalidades compostas

Como a composição aninha state e ações, você testa uma funcionalidade pai enviando ações da filha encapsuladas no caso do pai. As mesmas afirmações exaustivas se aplicam, verificando toda a árvore.

Aqui, um pai recebe um incremento de counter por meio de seu 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ção rápida: comportamento do TestStore

Relembre o que um TestStore padrão exige de suas afirmações.

Recapitulação: composição e testes

Você concluiu o conjunto de ferramentas do TCA:

  • Scope executa um redutor filho em uma parte do state e da ação pai, e vários redutores podem ser combinados em um único body.
  • store.scope fornece a uma visualização filha seu próprio store especializado.
  • TestStore verifica alterações exatas de state quando você envia ações com send.
  • Ele é exaustivo por padrão e usa receive para ações orientadas por efeitos.
  • Substitua as dependências com withDependencies para testes determinísticos.

Agora você pode criar, compor e testar rigorosamente funcionalidades com a Arquitetura Componível.

import ComposableArchitecture

// Recap: Scope to compose, TestStore to verify.
// Exhaustive assertions + injected fakes = confidence.
Grátis para começar

Aprenda Swift com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
122
Aulas
409

Perguntas Frequentes

A aula “Composição e Teste de Funcionalidades” é grátis?

Sim — o texto completo de “Composição e Teste de Funcionalidades” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Swift Academy, atualize para CoddyKit PRO. O curso de Swift Academy inclui 4 aulas no total.

O que vou aprender em “Composição e Teste de Funcionalidades”?

Combine redutores e teste exaustivamente. Você pratica Swift Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Swift Academy?

Nenhuma experiência prévia é necessária. Swift Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Composição e Teste de Funcionalidades”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Swift Academy?

Sim. Cada aula de Swift Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Estado, Ação e Redutor
  2. A Store e a Integração com SwiftUI
  3. Efeitos e Dependências
  4. Composição e Teste de Funcionalidades
← Voltar para Swift Academy