การประกอบและทดสอบความสามารถ
รวมรีดิวเซอร์และทดสอบให้ครอบคลุมทุกกรณี
การประกอบและทดสอบความสามารถ เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- สถานะ แอ็กชัน และรีดิวเซอร์
- Store และการผสานกับ SwiftUI
- เอฟเฟกต์และการพึ่งพา
- การประกอบและทดสอบความสามารถ