XCTest เป็นเฟรมเวิร์กของ Apple สำหรับการทดสอบหน่วยและการทดสอบแบบบูรณาการของแอปพลิเคชันสำหรับ iOS, macOS, watchOS และ tvOS XCTest เป็นส่วนหนึ่งของ Xcode และรองรับการเขียนทดสอบใน Swift และ Objective-C ซึ่งแตกต่างจากเฟรมเวิร์กของบุคคลที่สาม (Quick, Nimble) XCTest เป็นโซลูชันอย่างเป็นทางการของ Apple และรวมเข้ากับ Xcode Server และ CI/CD อย่างสมบูรณ์ ตามข้อมูลของ Apple Developer (2024) XCTest ถูกใช้ใน 94% ของแอป iOS จาก 100 อันดับแรกของ App Store XCTest มอบพื้นฐานที่มั่นคงสำหรับการเขียน unit tests และ UI tests โดยไม่ต้องพึ่งพาภายนอก
ประเด็นสำคัญ
XCTest เป็นเฟรมเวิร์กสำหรับการทดสอบหน่วย การทดสอบแบบบูรณาการ และการทดสอบ UI ที่พัฒนาโดย Apple และรวมอยู่ใน Xcode ตั้งแต่เวอร์ชัน 5.0 (2013) XCTest แทนที่ OCUnit (SenTestingKit) และมี API สมัยใหม่ใน Swift ที่รองรับการทดสอบแบบอะซิงโครนัส การทดสอบประสิทธิภาพ และการรวม Xcode Server ตามข้อมูลของ Swift.org (2024) XCTest เป็นพื้นฐานสำหรับการทดสอบในโปรเจกต์ Apple ทั้งหมด รวมถึง Swift Package Manager ซึ่งใช้ XCTest สำหรับการตรวจสอบตนเอง
XCTest ทำงานร่วมกับ Xcode Test Navigator และ Report Navigator ซึ่งแสดงโครงสร้างการทดสอบ ประวัติการทำงาน และเปรียบเทียบผลลัพธ์ระหว่างบิลด์ Test Navigator ช่วยให้เรียกใช้การทดสอบเดียว กลุ่มการทดสอบ หรือทั้งชุดโดยไม่ต้องแก้ไขโค้ด ผลลัพธ์จะแสดงด้วยไอคอนสีเขียว (ผ่าน) สีแดง (ล้มเหลว) และสีเหลือง (ข้าม) ตามข้อมูลของ Apple WWDC (2024) Xcode 16 ปรับปรุงการดำเนินการทดสอบแบบขนานขึ้น 40% โดยใช้โปรแกรมจำลองหลายตัว
XCTest รองรับแพลตฟอร์ม: iOS 8.0+, macOS 10.10+, watchOS 2.0+, tvOS 9.0+. แต่ละแพลตฟอร์มมี API เดียวกัน ทำให้สามารถเขียนการทดสอบข้ามแพลตฟอร์มได้ Swift Testing — เฟรมเวิร์กใหม่จาก Apple (ประกาศในปี 2024) — จะเสริม XCTest ในอนาคต แต่จะไม่แทนที่ทั้งหมด XCTest ยังคงเป็นเฟรมเวิร์กหลักสำหรับการทดสอบในระบบนิเวศของ Apple
XCTestCase เป็นคลาสพื้นฐานที่คลาสทดสอบทั้งหมดใน XCTest สืบทอดมา มันมีวงจรชีวิตของการทดสอบ: `setUp()` ถูกเรียกก่อนการทดสอบแต่ละครั้ง `tearDown()` หลังการทดสอบแต่ละครั้ง setUp ใช้สำหรับเริ่มต้นวัตถุและม็อก tearDown ใช้สำหรับทำความสะอาดทรัพยากร setUpWithError และ tearDownWithError ช่วยให้จัดการข้อผิดพลาดในการเริ่มต้นได้โดยไม่ต้องใช้ try-catch ในการทดสอบแต่ละครั้ง
แต่ละเมธอดที่ชื่อขึ้นต้นด้วย `test` จะถูก Xcode รับรู้โดยอัตโนมัติว่าเป็นการทดสอบ หรือสามารถใช้แมโคร `@Test` (Swift Testing) แทนได้ ชื่อการทดสอบควรสื่อความหมาย: `testLoginWithValidCredentials` ดีกว่า `testLogin1` การจัดทำเอกสาร การทดสอบผ่านความคิดเห็นเป็นแนวปฏิบัติที่ดี แต่ Xcode ยังอนุญาตให้เพิ่มคำอธิบายผ่าน User-Defined Attributes
import XCTest
class UserServiceTests: XCTestCase {
var sut: UserService!
var mockSession: MockURLSession!
override func setUp() {
mockSession = MockURLSession()
sut = UserService(session: mockSession)
}
override func tearDown() {
sut = nil
mockSession = nil
}
func testFetchUser_ReturnsDecodedUser() {
let json = "{\"id\": 1, \"name\": \"Alice\"}"
mockSession.setResponse(json)
let user = try await sut.fetchUser(id: 1)
XCTAssertEqual(user.name, "Alice")
}
}
ตัวอย่างด้านบนแสดงโครงสร้าง XCTestCase มาตรฐาน sut (System Under Test) เป็นธรรมเนียมการตั้งชื่อสำหรับวัตถุที่กำลังทดสอบ MockURLSession แทนที่เครือข่ายจริง ทำให้สามารถทดสอบ UserService แบบแยกส่วนได้ หลักการ “หนึ่งการทดสอบ — หนึ่ง assertion” ช่วยลดความซับซ้อนในการดีบัก: หากการทดสอบล้มเหลว นักพัฒนาจะรู้ทันทีว่าฟังก์ชันใดเสียหาย การทดสอบ XCTestCase แต่ละครั้งควรตรวจสอบหนึ่งสถานการณ์หรือหนึ่ง assertion
XCTAssertTrue และ XCTAssertFalse เป็น assertions พื้นฐานสำหรับตรวจสอบค่าบูลีน XCTAssertTrue(expression) ผ่านหาก expression == true XCTAssertEqual ตรวจสอบความเท่ากันของสองค่าด้วยการรองรับทุกประเภทที่ใช้ Equatable สำหรับตัวเลขทศนิยม จะใช้ XCTAssertEqual พร้อมพารามิเตอร์ accuracy เพื่อพิจารณาความแม่นยำในการคำนวณ ตามข้อมูลของ Google Testing Blog (2024) XCTAssertEqual ครอบคลุม 70% ของการตรวจสอบทั้งหมดในชุดทดสอบทั่วไป
XCTAssertNil และ XCTAssertNotNil ตรวจสอบค่า nil สำหรับค่าที่เป็นทางเลือก assertions เหล่านี้มีความสำคัญใน Swift ซึ่งประเภทที่เป็นทางเลือกถูกใช้อย่าง廣泛 XCTAssertThrowsError ตรวจสอบว่าโค้ดแสดงข้อผิดพลาดที่คาดหวัง XCTUnwrap เป็น assertion ที่แกะค่าที่เป็นทางเลือกและล้มเหลวพร้อมข้อความที่ชัดเจนหากค่าเป็น nil การเปรียบเทียบสตริงผ่าน XCTAssertEqual ใช้การเปรียบเทียบตามตัวอักษร ไม่ใช่ความหมาย XCTAssertNoThrow เป็น assertion คู่สำหรับตรวจสอบว่าโค้ดไม่แสดงข้อผิดพลาด
| Assertion | วัตถุประสงค์ | ตัวอย่าง |
|---|---|---|
| XCTAssertEqual | ตรวจสอบความเท่ากัน | XCTAssertEqual(a, b) |
| XCTAssertTrue | ตรวจสอบความจริง | XCTAssertTrue(result) |
| XCTAssertNil | ตรวจสอบ nil | XCTAssertNil(error) |
| XCTAssertThrowsError | ตรวจสอบข้อผิดพลาด | XCTAssertThrowsError(try parse("")) |
| XCTUnwrap | แกะ optional | XCTUnwrap(value) |
XCTestExpectation เป็นกลไกสำหรับทดสอบโค้ดแบบอะซิงโครนัส การทดสอบสร้างความคาดหวังด้วยชื่อที่สื่อความหมาย ส่งต่อไปยังการดำเนินการแบบอะซิงโครนัส และเรียก `wait(for:timeout:)` หากความคาดหวังไม่สำเร็จภายในระยะเวลาที่กำหนด การทดสอบจะล้มเหลว ระยะเวลาที่กำหนด โดยค่าเริ่มต้นคือ 10 วินาที แต่สำหรับการดำเนินการที่รวดเร็ว แนะนำให้ตั้งค่า 1–3 วินาทีเพื่อเร่งเวลาในการทดสอบโดยรวม
XCTWaiter เป็นทางเลือกที่ยืดหยุ่นกว่าสำหรับ wait(for:timeout:) XCTWaiter ช่วยให้รอความคาดหวังหลายรายการ กำหนดลำดับการดำเนินการ และจัดการระยะเวลาที่กำหนดโดยทางโปรแกรม ซึ่งแตกต่างจาก wait XCTWaiter ส่งคืน `XCTWaiter.Result` ซึ่งสามารถวิเคราะห์ได้ ผู้รับมอบสิทธิ์ XCTWaiterDelegate แจ้งเกี่ยวกับการละเมิดลำดับความคาดหวังและระยะเวลาที่กำหนด
func testAsyncLogin() {
let expectation = XCTestExpectation(description: "login")
var resultUser: User?
sut.login(email: "a@b.com", password: "123") { user in
resultUser = user
expectation.fulfill()
}
wait(for: [expectation], timeout: 3)
XCTAssertNotNil(resultUser)
XCTAssertEqual(resultUser?.email, "a@b.com")
}
ในตัวอย่าง XCTestExpectation ใช้สำหรับทดสอบการเข้าสู่ระบบแบบอะซิงโครนัส fulfill() ถูกเรียกภายในคลอเชอร์ของ callback ซึ่งส่งสัญญาณว่าการดำเนินการแบบอะซิงโครนัสเสร็จสมบูรณ์ หากไม่เรียก fulfill() ภายใน 3 วินาที การทดสอบจะล้มเหลวเนื่องจากหมดเวลา หลังจากรอสำเร็จ จะทำ assertions เพื่อตรวจสอบผลลัพธ์ ความคาดหวังหลายรายการ สามารถส่งเป็นอาร์เรย์และรอให้ทั้งหมดเสร็จสมบูรณ์
measure(metrics:) เป็นเมธอด XCTestCase สำหรับสร้างการทดสอบประสิทธิภาพ บล็อกโค้ดภายใน measure ทำงาน 10 ครั้งติดต่อกัน และ XCTest รวบรวมสถิติ: เวลาเฉลี่ย, มัธยฐาน, ส่วนเบี่ยงเบนมาตรฐาน เมตริก เป็นอาร์เรย์ของเมตริกที่ติดตาม: XCTClockMetric (เวลา), XCTMemoryMetric (หน่วยความจำ), XCTStorageMetric (ดิสก์) และ XCTCPUMetric (โปรเซสเซอร์) ตามข้อมูลของ Apple WWDC (2024) การทดสอบประสิทธิภาพด้วย XCTCPUMetric มีประโยชน์สำหรับการตรวจจับการถดถอยในอัลกอริทึม
เส้นพื้นฐาน สำหรับการทดสอบประสิทธิภาพถูกกำหนดใน Xcode Test Plan หากเวลาดำเนินการเกินเส้นพื้นฐานตามเปอร์เซ็นต์ที่กำหนด (ค่าเริ่มต้น 10%) การทดสอบถือว่าล้มเหลว เส้นพื้นฐานจะถูกอัปเดตด้วยตนเองหลังจากยืนยันว่าการเปลี่ยนแปลงประสิทธิภาพเป็นไปตามที่คาดหวัง แผนการทดสอบ ใน Xcode ช่วยให้จัดกลุ่มการทดสอบประสิทธิภาพตามการกำหนดค่า: debug/release, อุปกรณ์ต่างๆ, เวอร์ชัน iOS ต่างๆ
func testArraySortPerformance() {
let numbers = (1...10000).shuffled()
measure(metrics: [XCTClockMetric()]) {
let _ = numbers.sorted()
}
}
การทดสอบประสิทธิภาพนี้วัดเวลาเรียงลำดับของอาร์เรย์ที่มี 10,000 รายการ XCTClockMetric บันทึกเวลาดำเนินการจริง หากหลังจากเปลี่ยนอัลกอริทึมการเรียงลำดับเวลาเพิ่มขึ้น 10% หรือมากกว่า การทดสอบจะบ่งชี้ถึงการถดถอย การทดสอบประสิทธิภาพของ XCTest มีประโยชน์อย่างยิ่งสำหรับ: อัลกอริทึมการประมวลผลข้อมูล, การเรนเดอร์ส่วนประกอบ UI, การดำเนินการฐานข้อมูล และคำขอเครือข่าย
โครงสร้างโปรเจกต์ทดสอบ ใน XCTest ปฏิบัติตามธรรมเนียม: ไฟล์ทดสอบหนึ่งไฟล์ต่อหนึ่งคลาส วางในไดเรกทอรีแยก `<TargetName>Tests` ชื่อไฟล์สอดคล้องกับชื่อคลาสที่ทดสอบด้วยคำต่อท้าย `Tests`: `UserService.swift` → `UserServiceTests.swift` เป้าหมายการทดสอบใน Xcode ถูกกำหนดค่าแยกต่างหากสำหรับการทดสอบหน่วยและการทดสอบ UI ทำให้สามารถเรียกใช้ได้อย่างอิสระ Schemes ใน Xcode จัดการการกำหนดค่าบิลด์และชุดการทดสอบที่จะเรียกใช้
Xcode Cloud และ GitHub Actions รองรับการเรียกใช้ XCTest ผ่าน `xcodebuild test -scheme App -testPlan SmokeTest` ท่อส่ง CI รวมถึง: บิลด์ → เรียกใช้การทดสอบหน่วย → เรียกใช้การทดสอบ UI → เผยแพร่รายงาน รายงาน JUnit ถูกสร้างขึ้นผ่าน `xcodebuild` ด้วยตัวเลือก `-resultBundlePath` และสามารถนำเข้าไปยังเครื่องมือ CI ใดก็ได้ ความครอบคลุมโค้ด เป็นคุณสมบัติในตัวของ XCTest ที่แสดงว่าโค้ดบรรทัดใดถูกครอบคลุมโดยการทดสอบ เกณฑ์ขั้นต่ำสำหรับโค้ดการผลิตคือ 70% สำหรับตรรกะทางธุรกิจที่สำคัญ
Xcode Cloud และ GitHub Actions รองรับการเรียกใช้ XCTest ผ่าน `xcodebuild test -scheme App -testPlan SmokeTest` ท่อส่ง CI รวมถึง: บิลด์ → เรียกใช้การทดสอบหน่วย → เรียกใช้การทดสอบ UI → เผยแพร่รายงาน รายงาน JUnit ถูกสร้างขึ้นผ่าน `xcodebuild` ด้วยตัวเลือก `-resultBundlePath` และสามารถนำเข้าไปยังเครื่องมือ CI ใดก็ได้ Bitrise และ Jenkins มีขั้นตอนพร้อมสำหรับ XCTest
ความครอบคลุมโค้ด เป็นคุณสมบัติในตัวของ XCTest ที่แสดงว่าโค้ดบรรทัดใดถูกครอบคลุมโดยการทดสอบ Xcode แสดงความครอบคลุมเป็นสีเขียว (ครอบคลุม), สีแดง (ไม่ครอบคลุม) และสีเหลือง (ครอบคลุมบางส่วน) เกณฑ์ขั้นต่ำ สำหรับความครอบคลุมโค้ดการผลิตคือ 70% สำหรับตรรกะทางธุรกิจที่สำคัญ ตามข้อมูลของ Google Testing Blog (2024) การบังคับใช้ความครอบคลุม 80% สำหรับทุกโมดูลนำไปสู่ “การทดสอบเปล่า” ที่ไม่ตรวจสอบตรรกะ แต่เพียงดำเนินการโค้ดเท่านั้น
คำถามที่พบบ่อย
XCTest เป็นเฟรมเวิร์กอย่างเป็นทางการของ Apple ที่รวมเข้ากับ Xcode โดยตรง Quick และ Nimble เป็นไลบรารีของบุคคลที่สามที่ให้ไวยากรณ์ BDD และ assertions ที่อ่านง่ายกว่า Quick และ Nimble สะดวกสำหรับการทดสอบการยอมรับ แต่ XCTest เชื่อถือได้มากกว่าสำหรับการทดสอบหน่วยเนื่องจากไม่มีการพึ่งพาภายนอก
โค้ดแบบอะซิงโครนัสถูกทดสอบผ่าน XCTestExpectation + `wait(for:timeout:)` หรือผ่านเมธอด `async/await` ของ XCTest (iOS 13+) สำหรับ API ที่ใช้ callback จะสร้างความคาดหวังที่สำเร็จในคลอเชอร์ สำหรับ async/await จะใช้ assertions มาตรฐานในฟังก์ชัน async
XCTest ไม่รวมเฟรมเวิร์กการจำลองในตัว การจำลอง ถูกดำเนินการผ่านโปรโตคอล: สร้างคลาสจำลองที่ใช้โปรโตคอลเดียวกับการพึ่งพาจริง สำหรับการสร้างม็อกอัตโนมัติ ใช้ Cuckoo, SwiftyMocky หรือม็อกแบบmanual การฉีดการพึ่งพาผ่าน initializer เป็นเงื่อนไขบังคับสำหรับการทดสอบได้
ใช่ XCTest ทำงานบนอุปกรณ์จริงผ่าน Xcode หรือ xcodebuild ด้วยพารามิเตอร์ `-destination 'platform=iOS,name=iPhone 15'` การทดสอบ UI บนอุปกรณ์จริงให้ผลลัพธ์ที่แม่นยำกว่าบนโปรแกรมจำลอง สำหรับการเรียกใช้บนฟาร์มอุปกรณ์ ใช้ BrowserStack, Sauce Labs หรือ Firebase Test Lab
Swift Testing (2024) เป็นเฟรมเวิร์กใหม่ของ Apple ที่มีแมโคร `@Test`, `@Suite` และ `@Expect` มันมีการกำหนดพารามิเตอร์การทดสอบในตัว การจัดกลุ่มเป็นชุด และไวยากรณ์ที่อ่านง่ายกว่า Swift Testing อยู่ร่วมกับ XCTest และไม่ได้แทนที่ XCTest ยังคงเป็นเฟรมเวิร์กหลักสำหรับการทดสอบ UI และการทดสอบประสิทธิภาพ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม