Menguji resource dari awal sampai akhir
Membuat, membaca, memperbarui, dan menghapus, serta memeriksa bahwa setiap operasi benar-benar terjadi.
Sebagian besar API dibangun di sekitar resource: buku, pesanan, pengguna, faktur. Untuk setiap resource biasanya ada cara membuat, membaca, mengubah, dan menghapusnya. Penguji menyebutnya CRUD (create, read, update, delete), dan tes CRUD mengikuti satu data sepanjang hidupnya. Inilah hal pertama yang sebaiknya Anda uji pada resource baru mana pun, karena hampir semua fitur lain bergantung pada fungsinya.
Pelajaran ini memakai /api/Booksmasuk untuk melihat. Ia mendukung setiap operasi, dan halaman referensinya mendaftar field-field-nya. Method-nya sudah Anda kenal dari Method HTTP. Di sini pertanyaannya berbeda: bagaimana Anda membuktikan bahwa setiap operasi benar-benar melakukan apa yang diklaimnya?
Response adalah klaim, bukan bukti
Saat server menjawab POST dengan 201 dan salinan data, ia memberi tahu apa yang menurutnya sudah disimpan. Salinan itu biasanya dibangun dari objek yang sama yang hendak disimpan, jadi bisa terlihat sempurna meskipun penyimpanannya gagal, atau ada field yang hilang di jalan menuju database. Satu-satunya cara mengetahui apa yang disimpan adalah memintanya lagi dalam request terpisah.
Jadi tes CRUD bergantian antara melakukan sesuatu dan memeriksanya dengan pembacaan baru:
- Buat data. Periksa response-nya.
- Baca kembali berdasarkan ID. Bandingkan setiap field dengan yang Anda kirim.
- Temukan di endpoint daftar.
- Ganti dengan PUT. Baca kembali.
- Ubah satu field dengan PATCH. Baca kembali.
- Hapus. Baca kembali dan harapkan 404.
- Hapus lagi dan lihat apa yang terjadi.
Membuat
curl -i -X POST https://api.testingapis.com/api/Books \
-H "Content-Type: application/json" \
-d '{
"title": "Testing Web APIs",
"author": "Mark Winteringham",
"isbn": "9781617299537",
"genre": "testing",
"publishedYear": 2022,
"pages": 264,
"language": "English",
"price": 49.99,
"rating": 4.6,
"available": true,
"tags": ["api", "testing"]
}'HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
{
"message": "Book created successfully",
"data": {
"id": 26,
"title": "Testing Web APIs",
"author": "Mark Winteringham",
"isbn": "9781617299537",
"genre": "testing",
"publishedYear": 2022,
"pages": 264,
"language": "English",
"price": 49.99,
"rating": 4.6,
"available": true,
"tags": ["api", "testing"]
}
}Hal yang diperiksa pada response ini, kira-kira berdasarkan urutan pentingnya:
- Statusnya 201, bukan 200. Keduanya berarti sukses, tetapi 201 menyatakan ada resource baru, dan klien kadang bercabang berdasarkan itu.
- Ada
id, dan bertipe angka (atau tipe apa pun yang dijanjikan dokumentasi). Simpan. Setiap langkah berikutnya membutuhkannya. - Setiap field yang Anda kirim kembali dengan nilai yang sama. Bandingkan satu per satu, termasuk yang tampak membosankan. Harga
49.99yang kembali sebagai49.9899999atau"49.99"adalah bug. - Apakah header
Locationmenunjuk ke data baru. Banyak REST API mengirimnya bersama 201. API ini tidak. Itu lebih merupakan pilihan desain daripada cacat, tetapi Anda perlu tahu bagaimana API Anda dan menuliskannya dalam tes.
Sekarang lihat apa yang terjadi dengan kesalahan kecil di body. Field-nya bernama publishedYear, dan di sini dikirim sebagai year:
curl -X POST https://api.testingapis.com/api/Books \
-H "Content-Type: application/json" \
-d '{"title": "The Test Book", "author": "Ada Tester", "year": 2026}'{
"message": "Book created successfully",
"data": {
"id": 26,
"title": "The Test Book",
"author": "Ada Tester",
"isbn": "",
"genre": "",
"publishedYear": 0,
"pages": 0,
"language": "",
"price": 0,
"rating": 0,
"available": false,
"tags": []
}
}Server menerima request itu, mengabaikan year tanpa berkata apa-apa, dan menyimpan 0sebagai tahun. Tidak ada apa pun di kode status yang memberi tahu ada yang salah. Anda hanya menangkapnya dengan membandingkan data tersimpan dengan yang ingin Anda kirim. Field yang diabaikan diam-diam adalah salah satu sumber paling umum bug jenis "datanya salah tapi tidak ada yang tahu kenapa". Beberapa API justru menolak field tak dikenal; pelajaran berikutnya membahas itu.
Field yang diisi server
Beberapa field tidak berasal dari Anda: ID, dan pada banyak resource, timestamp seperti createdAt atau updatedAt. Anda tidak bisa membandingkannya dengan nilai tetap, karena berubah di setiap eksekusi. Uji sifat-sifatnya:
- ID-nya ada, bertipe benar, dan berbeda dari ID yang Anda buat sebelumnya.
createdAtadalah tanggal valid dalam format yang didokumentasikan dan berada dalam beberapa detik dari saat Anda mengirim request. Perhitungkan selisih jam antara mesin Anda dan server.updatedAtberubah setelah pembaruan, sedangkancreatedAttidak.- Mengirim
idAnda sendiri di body tidak membuat Anda bisa memilihnya. Dokumentasi Books menyatakan ID di body diabaikan. Coba dengan"id": 1lalu periksa bahwa buku 1 tidak tersentuh sesudahnya.
Membaca kembali
Ambil data itu dengan ID yang Anda dapat, dan bandingkan seluruh body dengan yang Anda kirim. Lalu periksa daftarnya. Di API ini Anda bisa mencari berdasarkan judul:
/api/Books?q=web%20apisJika buku Anda tidak ada di hasil, berarti response pembuatan berbohong, atau daftar membaca dari tempat yang berbeda dengan endpoint data tunggal (cache, indeks pencarian, replika baca). Semua itu terjadi di sistem sungguhan. Memeriksa daftar juga menangkap masalah yang lebih tersembunyi: data tersimpan tetapi tersaring keluar dari daftar, misalnya karena filter bawaan menyembunyikan data yang punya field kosong.
Memperbarui dengan PUT dan PATCH
PUT mengganti seluruh data. Apa pun yang Anda lewatkan akan direset, biasanya ke nilai kosong atau bawaan. Orang terus melupakan ini, baik di sisi klien maupun server, jadi ujilah dengan sengaja. Di sini sebuah buku diperbarui hanya dengan tiga field:
curl -X PUT https://api.testingapis.com/api/Books/27 \
-H "Content-Type: application/json" \
-d '{"title": "Lessons Learned in Software Testing", "author": "Cem Kaner", "price": 39.0}'{
"message": "Book updated successfully",
"data": {
"id": 27,
"title": "Lessons Learned in Software Testing",
"author": "Cem Kaner",
"isbn": "",
"genre": "",
"publishedYear": 0,
"pages": 0,
"language": "",
"price": 39.0,
"rating": 0,
"available": false,
"tags": []
}
}ISBN, jumlah halaman, tahun, dan tag hilang. Itu perilaku PUT yang benar. Jika server mempertahankan nilai lama, ia bertingkah seperti PATCH, dan klien yang mengandalkan PUT untuk mengosongkan field tidak akan pernah berhasil. Request yang sama juga mengirim "id": 1 di body; data itu tetap ber-ID 27, dan GET terpisah memastikan buku 1 tidak berubah.
PUT yang tidak memuat field wajib gagal dengan 400, sebagaimana mestinya: {"price": 35} saja mengembalikan Missing required field: title.
PATCH hanya mengubah field yang Anda kirim. API ini memakai JSON merge patch, dan response-nya mendaftar apa yang diubah dan apa yang diabaikan:
curl -X PATCH https://api.testingapis.com/api/Books/26 \
-H "Content-Type: application/merge-patch+json" \
-d '{"price": 35, "available": false, "colour": "blue", "id": 999}'{
"message": "Book patched successfully",
"changed": ["price", "available"],
"ignored": ["colour", "id"],
"data": {
"id": 26,
"title": "Testing Web APIs",
"author": "Mark Winteringham",
"isbn": "9781617299537",
"genre": "testing",
"publishedYear": 2022,
"pages": 264,
"language": "English",
"price": 35,
"rating": 4.6,
"available": false,
"tags": ["api", "testing"]
}
}Periksa bahwa kedua field yang disebut berubah, bahwa setiap field lain tetap bernilai lama, dan bahwa id yang diabaikan memang diabaikan. Lalu lakukan GET baru dan periksa semuanya lagi. PATCH yang memperbarui response tetapi tidak memperbarui data tersimpan memang jarang, tetapi saat terjadi, itulah jenis bug yang lolos ke produksi, karena klien menampilkan nilai baru sampai halaman dimuat ulang.
Hapus, lalu hapus lagi
$ curl -i -X DELETE https://api.testingapis.com/api/Books/26
HTTP/1.1 200 OK
{"message":"Book deleted successfully"}
$ curl -i https://api.testingapis.com/api/Books/26
HTTP/1.1 404 Not Found
{"status":404,"error":"Not Found","message":"Book with ID 26 does not exist."}
$ curl -i -X DELETE https://api.testingapis.com/api/Books/26
HTTP/1.1 404 Not Found
{"status":404,"error":"Not Found","message":"Book with ID 26 does not exist."}Setelah dihapus, GET harus mengembalikan 404. Periksa juga bahwa data itu hilang dari daftar, dan jumlahnya turun (/api/Books/countmasuk untuk melihat mengembalikan {"count": 25} pada data awal).
DELETE kedua adalah tempat API berbeda-beda. API ini mengembalikan 404. API lain mengembalikan 204 atau 200 pada setiap penghapusan, dengan alasan keadaan akhirnya (data itu tidak ada) sama saja. Keduanya bisa dibenarkan. Yang Anda cari adalah 500, atau 200 yang mengklaim menghapus sesuatu yang tidak ada. Kirim juga PUT dan PATCH ke ID yang sudah dihapus. Di sini PATCH mendapat 404, tetapi PUT dengan body tidak lengkap mendapat 400 karena field yang hilang sebelum server menyadari bahwa datanya tidak ada. Error mana yang menang saat dua sama-sama berlaku layak mendapat satu kalimat di dokumentasi, dan sering tidak ada.
Buat data Anda sendiri
Ada godaan untuk menguji terhadap data 1 karena ia selalu ada. Jangan. Siapa pun bisa mengubah atau menghapus buku 1 di server bersama, dan tes yang mengeditnya merusak setiap tes lain yang membacanya. Setiap tes sebaiknya membuat data yang dibutuhkannya, hanya bekerja dengan ID yang diberikan kepadanya, dan menghapusnya di akhir.
API ini tempat yang baik untuk melihat alasannya. Ia menyimpan datanya di memori dan dipakai bersama oleh semua penggunanya. Saat pelajaran ini ditulis, pengguna lain membuat buku dengan ID 26 dalam beberapa detik di antara salah satu penghapusan kami dan pembuatan berikutnya. Tes yang berasumsi "buku berikutnya akan ber-ID 26" akan gagal karena alasan yang tidak ada hubungannya dengan kode yang diuji.
Eksperimen yang sama menunjukkan hal lain. Setelah buku 26 dihapus, buku berikutnya yang dibuat mendapat ID 26 lagi. Server ini memberikan nomor bebas berikutnya, jadi ID dipakai ulang. Jika sistem lain menyimpan "buku 26" sebelum penghapusan, kini ia menunjuk ke buku yang berbeda. Apakah itu bug tergantung produknya, tetapi temuan semacam ini layak diangkat. Sebagian besar database tidak pernah memakai ulang ID karena alasan ini.
Endpoint massal
Banyak API membolehkan Anda membuat atau menghapus beberapa data dalam satu request. Pertanyaan yang menarik adalah apa yang terjadi saat sebagian item buruk.
curl -X POST https://api.testingapis.com/api/Books/bulk \
-H "Content-Type: application/json" \
-d '[
{"title": "Bulk A", "author": "Tester"},
{"title": "Bulk B", "author": "Tester"},
{"title": "", "author": "Tester"}
]'
{"status":400,"error":"Bad Request","message":"Item [2]: Missing required field: title"}Item ketiga punya judul kosong, jadi seluruh request gagal dan error-nya menyebut item itu berdasarkan indeks. Dokumentasi Books menyatakan tidak ada yang disimpan dalam kasus itu, dan Anda perlu memverifikasinya: periksa bahwa "Bulk A" tidak ada setelah request yang gagal. API yang menyimpan dua item pertama lalu gagal membuat klien tidak tahu apa yang harus diulang.
Penghapusan massal melaporkan ID mana yang ditemukan dan mana yang tidak:
curl -X DELETE "https://api.testingapis.com/api/Books/bulk?ids=27,28,5000"
{"message":"2 book record(s) deleted","deleted":[27,28],"notFound":[5000]}| Kasus massal | Yang diperiksa |
|---|---|
| Semua item valid | Setiap item disimpan, ID-nya berbeda-beda, jumlahnya naik sebanyak yang dikirim. |
| Satu item tidak valid | Tidak ada yang disimpan, atau response menyebut persis item mana yang disimpan. |
| Array kosong | 400 yang jelas, bukan 201 yang tidak membuat apa pun. |
| Melebihi batas (di sini 100) | 400 yang menyebut batasnya. |
| ID duplikat dalam penghapusan massal | Setiap data dihapus sekali, tanpa error dari kemunculan kedua. |
Latihan
Jalankan siklus hidup lengkap pada /api/Moviesmasuk untuk melihat, dengan memakai halaman referensinya untuk field-field-nya. Buat film dengan semua field terisi, lalu:
- Baca kembali dan bandingkan setiap field dengan yang Anda kirim.
- Temukan dengan
?q=dan satu kata dari judulnya. - PUT dengan hanya field wajib. Field mana yang direset?
- PATCH satu field dan kirim satu nama field yang tidak ada. Baca daftar
ignored. - Hapus, lalu GET, PATCH, dan DELETE lagi. Catat setiap status.
Akhiri dengan mencatat satu hal yang akan Anda tanyakan kepada developer (field yang diabaikan, ID yang dipakai ulang, error yang menang atas error lain). Memutuskan apa yang dilaporkan sama pentingnya dalam tes seperti request-nya.