XCTest — adalah framework Apple untuk pengujian modular dan integrasi aplikasi untuk iOS, macOS, watchOS dan tvOS. XCTest merupakan bagian dari Xcode dan mendukung penulisan tes di Swift dan Objective-C. Tidak seperti framework pihak ketiga (Quick, Nimble), XCTest adalah solusi resmi Apple dan terintegrasi penuh dengan Xcode Server dan CI/CD. Menurut Apple Developer (2024), XCTest digunakan di 94% aplikasi iOS dari top-100 App Store. XCTest menyediakan dasar yang stabil untuk menulis unit-test dan UI-test tanpa ketergantungan eksternal.
Poin Utama
XCTest — adalah framework untuk pengujian modular, integrasi dan UI, dikembangkan oleh Apple dan tertanam di Xcode sejak versi 5.0 (2013). XCTest menggantikan OCUnit (SenTestingKit) dan menyediakan API modern di Swift dengan dukungan untuk tes asinkron, tes performa dan integrasi dengan Xcode Server. Menurut Swift.org (2024), XCTest adalah dasar pengujian di semua proyek Apple, termasuk Swift Package Manager yang menggunakan XCTest untuk pengujian mandiri.
XCTest bekerja bersama dengan Xcode Test Navigator dan Report Navigator, yang menampilkan pohon tes, riwayat eksekusi dan membandingkan hasil antar build. Test Navigator memungkinkan menjalankan satu tes, grup tes atau seluruh rangkaian tanpa mengubah kode. Hasil ditampilkan sebagai ikon hijau (passed), merah (failed) dan kuning (skipped). Menurut Apple WWDC (2024), Xcode 16 meningkatkan eksekusi tes paralel sebesar 40% dengan menggunakan beberapa simulator.
XCTest mendukung platform: iOS 8.0+, macOS 10.10+, watchOS 2.0+, tvOS 9.0+. Untuk setiap platform tersedia API yang sama, memungkinkan penulisan tes lintas platform. Swift Testing — framework baru Apple (diumumkan tahun 2024) yang akan melengkapi XCTest di masa depan, tetapi tidak akan menggantikannya sepenuhnya. XCTest tetap menjadi framework utama untuk pengujian di ekosistem Apple.
XCTestCase — adalah kelas dasar yang diwarisi oleh semua kelas tes di XCTest. Ia menyediakan siklus hidup tes: `setUp()` dipanggil sebelum setiap tes, `tearDown()` — setelah setiap tes. SetUp digunakan untuk inisialisasi objek dan mock, tearDown — untuk membersihkan sumber daya. setUpWithError dan tearDownWithError memungkinkan penanganan kesalahan inisialisasi tanpa try-catch di setiap tes.
Setiap metode yang namanya dimulai dengan `test` secara otomatis dikenali oleh Xcode sebagai tes. Alternatifnya, makro `@Test` (Swift Testing) dapat digunakan. Nama tes harus deskriptif: `testLoginWithValidCredentials` lebih baik dari `testLogin1`. Dokumentasi tes melalui komentar adalah praktik yang baik, tetapi Xcode memungkinkan penambahan deskripsi melalui 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")
}
}
Contoh di atas menunjukkan struktur standar XCTestCase. sut (System Under Test) — konvensi penamaan objek yang diuji. MockURLSession menggantikan jaringan nyata, memungkinkan pengujian UserService secara terisolasi. Prinsip "satu tes — satu pemeriksaan" menyederhanakan debugging. Setiap tes XCTestCase harus memeriksa satu skenario atau satu pernyataan.
XCTAssertTrue dan XCTAssertFalse — asersi dasar untuk memeriksa nilai boolean. XCTAssertTrue(expression) lolos jika expression == true. XCTAssertEqual memeriksa kesamaan dua nilai dengan dukungan untuk semua tipe yang mengimplementasikan Equatable. Untuk angka floating-point digunakan XCTAssertEqual dengan parameter accuracy untuk memperhitungkan kesalahan perhitungan. Menurut Google Testing Blog (2024), XCTAssertEqual mencakup 70% dari semua pemeriksaan dalam rangkaian tes tipikal.
XCTAssertNil dan XCTAssertNotNil memeriksa nilai opsional untuk nil. Asersi ini sangat penting untuk Swift, di mana tipe opsional banyak digunakan. XCTAssertThrowsError memeriksa apakah kode melempar kesalahan yang diharapkan. XCTUnwrap — asersi yang mengeluarkan nilai opsional dan gagal dengan pesan yang jelas jika nilainya nil. Perbandingan string melalui XCTAssertEqual menggunakan perbandingan literal, bukan semantik. XCTAssertNoThrow — asersi pasangan untuk memeriksa bahwa kode tidak melempar kesalahan.
| Asersi | Tujuan | Contoh |
|---|---|---|
| XCTAssertEqual | Memeriksa kesamaan | XCTAssertEqual(a, b) |
| XCTAssertTrue | Memeriksa kebenaran | XCTAssertTrue(result) |
| XCTAssertNil | Memeriksa nil | XCTAssertNil(error) |
| XCTAssertThrowsError | Memeriksa kesalahan | XCTAssertThrowsError(try parse("")) |
| XCTUnwrap | Mengeluarkan optional | XCTUnwrap(value) |
XCTestExpectation — adalah mekanisme untuk menguji kode asinkron. Tes membuat ekspektasi dengan nama deskriptif, meneruskannya ke operasi asinkron dan memanggil `wait(for:timeout:)`. Jika ekspektasi tidak terpenuhi dalam batas waktu, tes gagal. Batas waktu secara default 10 detik, tetapi untuk operasi cepat disarankan mengatur 1–3 detik untuk mempercepat total waktu pengujian.
XCTWaiter — alternatif yang lebih fleksibel untuk wait(for:timeout:). XCTWaiter memungkinkan menunggu beberapa ekspektasi, mengatur urutan eksekusi dan menangani batas waktu secara programatis. Tidak seperti wait, XCTWaiter mengembalikan `XCTWaiter.Result` yang dapat dianalisis. Delegat XCTWaiterDelegate memberitahu tentang pelanggaran urutan ekspektasi dan batas waktu.
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")
}
Dalam contoh, XCTestExpectation digunakan untuk menguji login asinkron. fulfill() dipanggil di dalam callback, menandakan bahwa operasi asinkron telah selesai. Jika dalam 3 detik fulfill() tidak dipanggil — tes gagal dengan batas waktu. Setelah ekspektasi berhasil, asersi dijalankan untuk memeriksa hasil. Beberapa ekspektasi dapat diteruskan sebagai array dan menunggu semua terpenuhi.
measure(metrics:) — metode XCTestCase untuk membuat tes performa. Blok kode di dalam measure dijalankan 10 kali berturut-turut, dan XCTest mengumpulkan statistik: waktu rata-rata, median, deviasi standar. Metrics — array metrik yang dilacak: XCTClockMetric (waktu), XCTMemoryMetric (memori), XCTStorageMetric (disk) dan XCTCPUMetric (prosesor). Menurut Apple WWDC (2024), tes performa dengan XCTCPUMetric berguna untuk mendeteksi regresi dalam algoritma.
Baseline (garis dasar) untuk tes performa ditetapkan di Xcode Test Plan. Jika waktu eksekusi melebihi baseline dengan persentase yang ditentukan (default 10%), tes dianggap gagal. Baseline diperbarui secara manual setelah dikonfirmasi bahwa perubahan performa diharapkan. Test Plan di Xcode memungkinkan pengelompokan tes performa berdasarkan konfigurasi: debug/release, perangkat berbeda, versi iOS berbeda.
func testArraySortPerformance() {
let numbers = (1...10000).shuffled()
measure(metrics: [XCTClockMetric()]) {
let _ = numbers.sorted()
}
}
Tes performa ini mengukur waktu pengurutan array 10000 elemen. XCTClockMetric mencatat waktu eksekusi nyata. Jika setelah mengubah algoritma pengurutan waktu meningkat 10% atau lebih, tes akan menunjukkan regresi. Tes performa XCTest sangat berguna untuk: algoritma pemrosesan data, rendering komponen UI, operasi basis data dan permintaan jaringan.
Struktur proyek tes di XCTest mengikuti konvensi: satu file tes per kelas, ditempatkan di direktori terpisah `<TargetName>Tests`. Nama file sesuai dengan nama kelas yang diuji dengan akhiran `Tests`: `UserService.swift` → `UserServiceTests.swift`. Test Targets di Xcode dikonfigurasi secara terpisah untuk unit-test dan UI-test, memungkinkan eksekusi independen. Schemes di Xcode mengelola konfigurasi build dan rangkaian tes yang dijalankan.
Xcode Cloud dan GitHub Actions mendukung eksekusi XCTest melalui `xcodebuild test -scheme App -testPlan SmokeTest`. Pipeline CI meliputi: build → menjalankan unit-test → menjalankan UI-test → mempublikasikan laporan. Laporan JUnit dihasilkan oleh `xcodebuild` dengan opsi `-resultBundlePath` dan dapat diimpor ke alat CI apa pun. Code Coverage — fungsi bawaan XCTest yang menunjukkan baris kode mana yang tercakup oleh tes. Ambang minimum cakupan untuk kode produksi — 70% untuk logika bisnis kritis.
Xcode Cloud dan GitHub Actions mendukung eksekusi XCTest melalui `xcodebuild test -scheme App -testPlan SmokeTest`. Pipeline CI meliputi: build → menjalankan unit-test → menjalankan UI-test → mempublikasikan laporan. Laporan JUnit dihasilkan oleh `xcodebuild` dengan opsi `-resultBundlePath` dan dapat diimpor ke alat CI apa pun. Bitrise dan Jenkins memiliki langkah siap pakai untuk XCTest.
Code Coverage — fungsi bawaan XCTest yang menunjukkan baris kode mana yang tercakup oleh tes. Xcode menampilkan cakupan dalam warna hijau (tercakup), merah (tidak tercakup) dan kuning (sebagian tercakup). Ambang minimum cakupan untuk kode produksi — 70% untuk logika bisnis kritis. Menurut Google Testing Blog (2024), mewajibkan 80% cakupan untuk semua modul menyebabkan munculnya "tes kosong" yang tidak memeriksa logika, hanya mengeksekusi kode.
Pertanyaan yang Sering Diajukan
XCTest — framework resmi Apple dengan integrasi langsung di Xcode. Quick dan Nimble — pustaka pihak ketiga yang menyediakan sintaks BDD dan asersi yang lebih mudah dibaca. Quick dan Nimble nyaman untuk Acceptance Testing, tetapi XCTest lebih andal untuk unit-test karena tidak memiliki ketergantungan eksternal.
Kode asinkron diuji melalui XCTestExpectation + `wait(for:timeout:)` atau melalui metode `async/await` XCTest (iOS 13+). Untuk API berbasis callback dibuat ekspektasi yang dipanggil di dalam closure. Untuk async/await digunakan asersi standar di fungsi async.
XCTest tidak memiliki framework bawaan untuk mock. Mocking dilakukan melalui protokol: kelas mock dibuat yang mengimplementasikan protokol yang sama dengan dependensi nyata. Untuk menghasilkan mock otomatis digunakan Cuckoo, SwiftyMocky atau mock manual. Dependency Injection melalui inisialisasi — syarat wajib untuk pengujian.
Ya, XCTest dijalankan di perangkat nyata melalui Xcode atau xcodebuild dengan parameter `-destination 'platform=iOS,name=iPhone 15'`. UI-test di perangkat nyata memberikan hasil yang lebih akurat daripada di simulator. Untuk eksekusi di farm perangkat digunakan BrowserStack, Sauce Labs atau Firebase Test Lab.
Swift Testing (2024) — framework baru Apple dengan makro `@Test`, `@Suite` dan `@Expect`. Ia menyediakan parametrisasi tes bawaan, pengelompokan dalam suite dan sintaks yang lebih mudah dibaca. Swift Testing hidup berdampingan dengan XCTest dan tidak menggantikannya. XCTest tetap menjadi framework utama untuk UI-test dan tes performa.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga