Swift Testing คือเฟรมเวิร์กการทดสอบยุคใหม่ของ Apple ที่ออกแบบมาแทนที่ XCTest โดยใช้ Swift macros อย่าง @Test และ #expect เพื่อให้การเขียน test สั้นลง อ่านง่ายขึ้น และรองรับ Swift Concurrency แบบเนทีฟ ในปี 2026 Swift Testing ได้กลายเป็นมาตรฐานหลักที่มาพร้อม Xcode 26 และ Swift 6.2 พร้อมฟีเจอร์ครบถ้วนทั้ง parameterized tests, traits, และ test suites สำหรับโปรเจกต์ iOS, macOS, และ Swift Server-side ทุกประเภท
Swift Testing ใช้ @Test macro แทนการตั้งชื่อฟังก์ชันขึ้นต้นด้วย test เหมือน XCTest ทำให้ตั้งชื่อได้อิสระและมี display name แบบภาษาไทยได้
Swift Testing คืออะไร และทำไมต้องเปลี่ยนจาก XCTest?
Swift Testing คือเฟรมเวิร์กการทดสอบโอเพนซอร์สที่ทีม Swift พัฒนาขึ้นตั้งแต่ปี 2024 และผนวกเข้าเป็นส่วนหนึ่งของ Swift Toolchain อย่างเป็นทางการตั้งแต่ Xcode 16 เป็นต้นมา ในปี 2026 กับ Xcode 26 มันไม่ใช่ตัวเลือกเสริมอีกต่อไป แต่กลายเป็นค่าเริ่มต้นเมื่อสร้างโปรเจกต์ใหม่ ผมได้ย้ายทีมที่ผมดูแลจาก XCTest มาใช้ Swift Testing ตั้งแต่ปีที่แล้ว และพบว่าโค้ด test สั้นลงเฉลี่ย 30-40% เพราะไม่ต้องเขียน boilerplate ยาว ๆ อีกต่อไป
เหตุผลหลักที่ Apple ผลักดัน Swift Testing มีสามข้อ หนึ่ง คือการใช้ macros ทำให้ error message ชัดเจนกว่าเดิมมาก เพราะ compiler สามารถขยาย #expect(a == b) ให้เห็นค่าซ้ายและขวาโดยอัตโนมัติ ไม่ต้องเขียน message เพิ่มเอง สอง คือการรองรับ Swift Concurrency แบบเนทีฟ ไม่ต้องเขียน expectation กับ fulfill() ที่ดูเทอะทะเหมือน XCTest และสาม คือระบบ traits ที่ทำให้จัดกลุ่ม test ด้วย tags, บันทึกลิงก์ bug และตั้ง timeout ต่อ test ได้โดยไม่ต้องดัดแปลง test runner
นอกจากนี้ Swift Testing ยังทำงานคู่ขนานกับ XCTest ในโปรเจกต์เดียวกันได้ทันที คุณไม่จำเป็นต้องย้ายทุกอย่างในคราวเดียว ค่อย ๆ เขียน test ใหม่ด้วย Swift Testing แล้วปล่อย test เก่าไว้ก่อนก็ได้ ทำให้การเปลี่ยนผ่านเป็นเรื่องไม่เจ็บปวด
เริ่มต้นใช้งาน Swift Testing ใน Xcode 26
Swift Testing มาพร้อม Xcode 26 โดยไม่ต้องติดตั้งอะไรเพิ่ม แค่สร้างโปรเจกต์ใหม่ก็จะพบว่า test target ถูกตั้งค่าเป็น Swift Testing เป็นค่าเริ่มต้นแล้ว หากคุณมีโปรเจกต์เดิมที่ใช้ XCTest อยู่ ให้เปิด test target แล้วเพิ่มไฟล์ Swift ใหม่ปกติ ไม่ต้องสร้าง target แยก
สำหรับโปรเจกต์ Swift Package Manager ให้เพิ่ม dependency ใน Package.swift ดังนี้:
ตั้งแต่ Swift 6.0 เป็นต้นมา Swift Testing รวมอยู่ใน Swift Toolchain โดยตรง คุณไม่ต้องประกาศ dependency แยกอีก แค่ import Testing ในไฟล์ test ก็ใช้งานได้ทันที เมื่อจะรัน test ให้กด Cmd+U ใน Xcode หรือใช้คำสั่ง swift test ใน Terminal ผลลัพธ์จะแสดงในรูปแบบใหม่ที่จัดกลุ่มตาม Suite และแสดง display name ที่คุณตั้ง
@Test macro และการเขียน test แรกของคุณ
หัวใจของ Swift Testing คือ @Test macro ที่มาแทนที่ธรรมเนียมการตั้งชื่อฟังก์ชันขึ้นต้นด้วย test ใน XCTest ข้อดีที่เห็นได้ชัดคือคุณตั้งชื่อฟังก์ชันเป็นภาษาอังกฤษปกติได้ และยังใส่ display name แบบภาษาไทยให้แสดงใน Xcode ได้อีกด้วย
สังเกตว่าเราไม่ต้องสร้าง class ที่ inherit จาก XCTestCase อีกต่อไป ฟังก์ชัน test เป็น global function หรือ method ใน struct ก็ได้ ทำให้จัดระเบียบไฟล์ได้ยืดหยุ่นกว่าเดิมมาก และเนื่องจาก Swift Testing สร้าง instance ใหม่ต่อ test ทุกครั้ง ไม่มี state รั่วระหว่าง test เหมือน XCTest ที่แชร์ instance ในบางกรณี ตามที่อธิบายไว้ในเอกสารทางการของ Apple เรื่อง Swift Testing
สำหรับผู้ที่ทำงานกับ concurrency อยู่ เราแนะนำให้อ่านคู่มือ Swift 6.2 Approachable Concurrencyควบคู่กันไป เพราะ Swift Testing รองรับ async test เต็มรูปแบบและใช้ pattern เดียวกันกับโค้ด production
เมื่อมีจำนวนอาร์กิวเมนต์มาก ผมมักแยกออกไปเป็น static property เพื่อให้อ่านง่าย และสามารถแชร์ระหว่าง test หลายตัวได้ ในทีมของผมพบว่าการใช้ parameterized tests ลดจำนวนไฟล์ test ลงได้ครึ่งหนึ่งในกรณีที่มี validation logic ซับซ้อนหลายเงื่อนไข
@Suite และการจัดกลุ่ม test
@Suite macro ใช้จัดกลุ่ม test ที่เกี่ยวข้องกันไว้ใน struct หรือ class เดียวกัน คล้ายกับ XCTestCase แต่ยืดหยุ่นกว่ามาก และรองรับการซ้อน suite ภายในกันได้
จุดเปลี่ยนสำคัญคือ Swift Testing สร้าง instance ใหม่ของ Suite struct ก่อน test แต่ละตัว ทำให้ init ทำหน้าที่แทน setUp() โดยธรรมชาติ และไม่จำเป็นต้องมี tearDown() เพราะ Swift จะเรียก deinit ให้เองหลัง test จบ นี่เป็น pattern ที่ปลอดภัยต่อ state และเหมาะกับ Swift Concurrency มากกว่า
Traits: Tags, .disabled, .bug และ .timeLimit
Traits คือระบบเมทาดาต้าที่ทำให้ควบคุมพฤติกรรมของ test ได้ละเอียด โดยไม่ต้องแทรก if statement ในโค้ด test เลย traits ที่ใช้บ่อยที่สุดมีดังนี้
extension Tag {
@Tag static var integration: Self
@Tag static var slow: Self
}
@Test(
"ดึงข้อมูลจาก API จริง",
.tags(.integration, .slow),
.timeLimit(.minutes(1)),
.bug("https://github.com/company/repo/issues/1234", "รอ backend fix")
)
func fetchLiveData() async throws {
let data = try await api.fetchLiveMetrics()
#expect(data.count > 0)
}
@Test("ฟีเจอร์ที่ยังไม่พร้อม", .disabled("รอ backend release เดือนหน้า"))
func upcomingFeature() {
// test นี้จะข้ามไปโดยอัตโนมัติ
}
Trait ที่มีประโยชน์มากคือ .bug() ที่ผูก test เข้ากับ issue tracker โดยตรง เมื่อ test ล้มเหลว Xcode จะแสดงลิงก์ให้คลิกไปดู issue นั้นได้ทันที ส่วน .tags() ช่วยกรอง test เวลารันบน CI เช่นรัน .tags(.integration) เฉพาะกลางคืน และรัน .tags(.unit) ทุก commit ตามที่แนะนำในคู่มืออย่างเป็นทางการของ Swift Testing
.timeLimit() ใช้จำกัดเวลาต่อ test เพื่อป้องกัน test hang บน CI ซึ่งเป็นปัญหาที่พบบ่อยเมื่อทำงานกับโค้ด async ที่ deadlock
ต้องเลิกใช้ XCTest เลยไหมเมื่อเปลี่ยนมา Swift Testing?
ไม่ต้อง Swift Testing ทำงานคู่ขนานกับ XCTest ในโปรเจกต์เดียวกันได้ เขียน test ใหม่ด้วย Swift Testing โดยไม่กระทบ test เก่าเลย และยังต้องใช้ XCTest สำหรับ UI tests, performance tests และ Objective-C-based tests
Swift Testing รองรับ mocking หรือไม่?
Swift Testing ไม่ได้ให้ mocking framework ในตัว แต่ทำงานได้กับทุก mocking library ที่คุณใช้อยู่ เช่น Cuckoo, Mockingbird หรือ hand-rolled protocol-based mocks ที่พบบ่อยในโค้ด Swift สมัยใหม่
รัน test เฉพาะบางกลุ่มด้วย tags ได้อย่างไร?
ใน Xcode 26 กด Cmd+Shift+U แล้วเลือก tag ที่ต้องการรัน หรือใน command line ใช้ swift test --filter tag:integration เพื่อรันเฉพาะ test ที่มี trait .tags(.integration)
Swift Testing ใช้บน Linux ได้หรือไม่?
ได้ Swift Testing เป็นโอเพนซอร์สและรองรับ Linux, Windows, macOS, และ Apple platforms ทั้งหมด ทำให้เหมาะกับโปรเจกต์ Swift Server-side ด้วย เพียงระบุ swift-tools-version 6.0 ขึ้นไปใน Package.swift