0Pricing
Swift Academy · บทเรียน

การประกอบและทดสอบความสามารถ

รวมรีดิวเซอร์และทดสอบให้ครอบคลุมทุกกรณี

การประกอบและทดสอบความสามารถ เป็นบทเรียน Swift Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Swift Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

การประกอบฟีเจอร์

แอปจริงสร้างขึ้นจากฟีเจอร์ขนาดเล็กจำนวนมาก TCA ช่วยให้ฟีเจอร์แม่ฝังฟีเจอร์ลูกไว้ได้ เพื่อให้แต่ละฟีเจอร์มีหน้าที่เฉพาะ จากนั้นจึงรวมเข้าด้วยกันเป็นองค์รวมที่ใหญ่ขึ้น เครื่องมือสำหรับงานนี้คือ Scope รวมถึงการฝัง state และแอ็กชันของฟีเจอร์ลูก.

การประกอบนี่เองที่เป็นที่มาของชื่อสถาปัตยกรรมแบบประกอบได้.

import ComposableArchitecture

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

การฝัง state ของฟีเจอร์ลูก

State ของฟีเจอร์แม่จะเก็บ state ของฟีเจอร์ลูกไว้เป็นพร็อพเพอร์ตี ส่วน enum Action ของฟีเจอร์แม่จะมีกรณีหนึ่งที่ห่อแอ็กชันของฟีเจอร์ลูกไว้ การซ้อนกันนี้สะท้อนโครงสร้างที่มุมมองซ้อนกัน.

ในที่นี้ ฟีเจอร์แม่เก็บฟีเจอร์ลูกที่เป็นตัวนับไว้.

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 เรียกใช้รีดิวเซอร์ของฟีเจอร์ลูกกับส่วนของ state และแอ็กชันของฟีเจอร์แม่ที่เป็นของฟีเจอร์ลูก คุณระบุเส้นทางคีย์ของ state และเส้นทางกรณีของแอ็กชัน จากนั้นจึงระบุรีดิวเซอร์ของฟีเจอร์ลูก.

วิธีนี้จะเชื่อมฟีเจอร์ลูกเข้ากับวงจรอัปเดตของฟีเจอร์แม่.

import ComposableArchitecture

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

การรวมรีดิวเซอร์หลายตัว

body ของฟีเจอร์สามารถแสดงรายการรีดิวเซอร์หลายตัวได้ TCA จะเรียกใช้รีดิวเซอร์ตามลำดับสำหรับแต่ละแอ็กชัน วิธีนี้ช่วยให้คุณวาง Scope หนึ่งรายการหรือมากกว่าควบคู่กับ Reduce ของฟีเจอร์แม่ได้.

ในที่นี้ ฟีเจอร์ลูกสองตัวและตรรกะของฟีเจอร์แม่ทำงานร่วมกัน.

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

การกำหนดขอบเขตคลังสถานะสำหรับมุมมองลูก

ในส่วนของมุมมอง มุมมองลูกต้องใช้คลังสถานะที่ปรับเฉพาะให้กับฟีเจอร์ลูก ฟีเจอร์แม่สร้างคลังนี้ด้วย store.scope โดยเชื่อมไปยัง state และแอ็กชันของฟีเจอร์ลูก.

ในที่นี้ มุมมองแม่ส่งคลังสถานะที่กำหนดขอบเขตแล้วให้มุมมองตัวนับ.

import ComposableArchitecture
import SwiftUI

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

การแนะนำ TestStore

TestStore ช่วยให้คุณยืนยันได้อย่างแม่นยำว่าฟีเจอร์ทำงานอย่างไร คุณส่งแอ็กชันและอธิบายว่า state ต้องเปลี่ยนอย่างไร หากผลจริงแตกต่าง การทดสอบจะล้มเหลว การตรวจสอบอย่างครบถ้วนนี้ช่วยจับการถดถอยที่ละเอียดอ่อนได้.

คุณสร้างมันได้เหมือนคลังสถานะทั่วไป โดยระบุ state เริ่มต้นและรีดิวเซอร์.

import ComposableArchitecture
import XCTest

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

การยืนยันการเปลี่ยนแปลงของ state

เมื่อคุณส่งแอ็กชันไปยัง TestStore คุณจะส่งโคลเชอร์ที่อธิบายการเปลี่ยนแปลงที่คาดไว้ด้วย การทดสอบจะผ่านก็ต่อเมื่อ state เปลี่ยนแปลงตรงตามนั้นทุกประการ.

ในที่นี้ การเพิ่มค่าต้องทำให้ตัวนับเปลี่ยนจาก 0 เป็น 1.

import ComposableArchitecture
import XCTest

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

ความครบถ้วน

โดยค่าเริ่มต้น TestStore จะตรวจสอบอย่างครบถ้วน คุณต้องระบุการเปลี่ยนแปลงของ state ทุกครั้งและทุกแอ็กชันที่เอฟเฟกต์ส่งกลับมา การเปลี่ยนแปลงที่ไม่ได้จัดการจะทำให้การทดสอบล้มเหลว ซึ่งช่วยให้การยืนยันของคุณตรงกับความเป็นจริง.

คุณผ่อนข้อกำหนดนี้ได้ด้วย store.exhaustivity = .off สำหรับการทดสอบที่เน้นเฉพาะจุด.

import ComposableArchitecture

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

การรับแอ็กชันจากเอฟเฟกต์

เมื่อเอฟเฟกต์ส่งแอ็กชันกลับมา การทดสอบต้องใช้ receive เพื่อรับแอ็กชันนั้นและยืนยัน state ที่เกิดขึ้น วิธีนี้พิสูจน์ว่าลำดับการทำงานแบบอะซิงโครนัสทำงานได้ตั้งแต่ต้นจนจบ.

ในที่นี้ การโหลดใหม่จะกระตุ้นการตอบกลับที่การทดสอบรอรับและตรวจสอบ.

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

การแทนที่การพึ่งพาในการทดสอบ

เพื่อให้เอฟเฟกต์ทำงานได้อย่างกำหนดผลลัพธ์แน่นอน ให้แทนที่การพึ่งพาเมื่อสร้าง TestStore คุณสามารถระบุไคลเอนต์จำลองหรือนาฬิกาสำหรับการทดสอบ เพื่อให้ผลลัพธ์คาดเดาได้.

ในที่นี้ ไคลเอนต์สำหรับตัวเลขถูกแทนที่ด้วยไคลเอนต์ที่คืนค่า 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 }
}

การทดสอบฟีเจอร์ที่ประกอบกัน

เนื่องจากการประกอบฟีเจอร์ทำให้ state และแอ็กชันซ้อนกัน คุณจึงทดสอบฟีเจอร์แม่ด้วยการส่งแอ็กชันของฟีเจอร์ลูกที่ห่ออยู่ในกรณีของฟีเจอร์แม่ การยืนยันอย่างครบถ้วนแบบเดียวกันจะตรวจสอบโครงสร้างทั้งหมด.

ในที่นี้ ฟีเจอร์แม่ได้รับแอ็กชันเพิ่มค่าของตัวนับผ่าน 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
    }
}

ตรวจสอบอย่างรวดเร็ว: การทำงานของ TestStore

ทบทวนว่า TestStore ที่ใช้ค่าเริ่มต้นต้องการอะไรจากการยืนยันของคุณ.

สรุป: การประกอบและการทดสอบ

คุณเรียนรู้ชุดเครื่องมือของ TCA ครบแล้ว:

  • Scope เรียกใช้รีดิวเซอร์ของฟีเจอร์ลูกกับส่วนหนึ่งของ state และแอ็กชันของฟีเจอร์แม่ และสามารถรวมรีดิวเซอร์หลายตัวไว้ใน body เดียวกันได้
  • store.scope มอบคลังสถานะเฉพาะของฟีเจอร์ลูกให้มุมมองลูก
  • TestStore ยืนยันการเปลี่ยนแปลงของ state ได้อย่างแม่นยำเมื่อคุณ send แอ็กชัน
  • โดยค่าเริ่มต้นจะตรวจสอบอย่างครบถ้วน และใช้ receive สำหรับแอ็กชันที่ขับเคลื่อนด้วยเอฟเฟกต์
  • แทนที่การพึ่งพาด้วย withDependencies เพื่อให้การทดสอบมีผลลัพธ์ที่กำหนดแน่นอน

ตอนนี้คุณสามารถสร้าง ประกอบ และทดสอบฟีเจอร์ต่าง ๆ ด้วยสถาปัตยกรรมแบบประกอบได้อย่างเข้มงวดแล้ว.

import ComposableArchitecture

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

คำถามที่พบบ่อย

บทเรียน “การประกอบและทดสอบความสามารถ” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การประกอบและทดสอบความสามารถ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Swift Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การประกอบและทดสอบความสามารถ”

รวมรีดิวเซอร์และทดสอบให้ครอบคลุมทุกกรณี คุณปฏิบัติ Swift Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Swift Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Swift Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “การประกอบและทดสอบความสามารถ” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Swift Academy นี้ได้ไหม

ได้ บทเรียน Swift Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. สถานะ แอ็กชัน และรีดิวเซอร์
  2. Store และการผสานกับ SwiftUI
  3. เอฟเฟกต์และการพึ่งพา
  4. การประกอบและทดสอบความสามารถ
← กลับไปที่ Swift Academy