NỘI DUNG BÀI HỌC
🕵️ Bản chất thực sự của API
📬 Giải phẫu Request/Response:
🔄 Phân biệt PUT vs PATCH
🛠️ Vũ khí cURL & Postman
🏍️ Playwright "Shipper"
💻 Thực hành CRUD đầu tay
🩺 Soft Assertion
🕵️ Phần 1: BẢN CHẤT CỦA API (UNDER THE HOOD)
API Là Gì? (Giải thích bằng Mô hình Nhà Hàng)
Hãy tưởng tượng một Nhà Hàng Sang Trọng:
-
Khách hàng (Frontend/Client): Là người ngồi bàn ăn. Họ chỉ biết nhìn menu (Giao diện UI) và gọi món. Họ không biết (và không cần biết) bếp trưởng nấu nướng thế nào.
-
Nhà bếp (Backend & Database): Là nơi chứa nguyên liệu (Dữ liệu/SQL) và chế biến món ăn.
-
Bồi bàn (API): Đây chính là nhân vật quan trọng nhất!
Quy trình hoạt động:
-
Khách hàng không thể xông thẳng vào bếp để tự lấy thịt bò (Sẽ rất mất vệ sinh và lộn xộn -> Giống như việc FE không thể chọc thẳng vào Database).
-
Khách hàng gọi bồi bàn: "Cho tôi 1 phần Bò bít tết" (Request).
-
Bồi bàn ghi chép, mang order vào bếp.
-
Bếp nấu xong, bồi bàn mang đĩa thức ăn ra cho khách (Response).
👉 Kết luận: API (Bồi bàn) là người vận chuyển yêu cầu và dữ liệu giữa Giao diện (Khách) và Server (Bếp).
API có phải là "Mô hình hóa SQL"? (Câu hỏi của bạn)
Nhiều bạn thấy:
-
GETgiốngSELECT -
POSTgiốngINSERT -
DELETE giống DELETE
=> Nên nghĩ API chỉ là cái vỏ của SQL.
❌ SAI LẦM! Nếu API chỉ là SQL, thì Hacker sẽ hack nát hệ thống trong 1 nốt nhạc.
API là một Lớp Bảo Vệ & Logic (Business Logic Layer):
Trước khi câu lệnh SQL được chạy, API phải làm hàng tá việc:
-
Authentication (Xác thực): "Thằng đang gọi lệnh này là ai? Có phải Admin không hay là thằng ất ơ?"
-
Validation (Kiểm tra): "Nó gửi email có đúng định dạng @gmail.com không? Mật khẩu có đủ 6 ký tự không?"
-
Transformation (Chế biến): User gửi ngày sinh là
01/01/2000, nhưng Database cần lưu là2000-01-01. API phải đứng giữa sửa lại format.
👉 Ví dụ :
-
SQL: Là con dao sắc bén trong bếp.
-
API: Là đầu bếp cầm con dao đó. Đầu bếp biết cách dùng dao để cắt thịt (Dữ liệu) sao cho đẹp và an toàn. Nếu đưa dao cho khách (Frontend), khách sẽ tự đứt tay.
Ngôn ngữ chung: JSON (Tiếng Anh của Web)
FE (JavaScript) và BE (Java/Python/C#) là hai người nói hai thứ tiếng khác nhau. Làm sao họ hiểu nhau?
Họ thống nhất dùng một ngôn ngữ chung, đó là JSON (JavaScript Object Notation).
-
Request (Gửi đi):
{ "mon_an": "Pho Bo", "so_luong": 2, "khong_hanh": true } -
Response (Trả về):
{ "status": "Thanh cong", "thoi_gian_cho": "15 phut" }
Giải phẫu một API Request (Mổ xẻ)
Khi Playwright (hay Postman) gửi 1 yêu cầu, nó bao gồm 4 phần tử chính. Hãy tưởng tượng đây là một Bức Thư:
1️⃣ Method (Hành động muốn làm)
Dán tem lên bì thư để định danh loại hành động:
-
GET: Chỉ xin xem dữ liệu (Không sửa đổi gì). VD: Xem danh sách sản phẩm.
-
POST: Tạo mới. VD: Đăng ký tài khoản.
-
PUT/PATCH: Sửa đổi. VD: Đổi mật khẩu, cập nhật profile.
-
DELETE: Xóa sổ. VD: Xóa giỏ hàng.
2️⃣ Endpoint (Địa chỉ nhà)
Gửi thư thì phải có địa chỉ.
-
Base URL:
https://api.facebook.com(Địa chỉ tòa nhà). -
Path:
/users/profile(Số phòng cụ thể).
3️⃣ Headers (Thông tin phụ/Meta data)
Những thứ viết bên ngoài phong bì thư:
-
Content-Type: application/json(Báo trước: "Trong thư này tao viết bằng JSON nhé"). -
Authorization: Bearer abc...xyz(Vé vào cửa: "Tao là VIP, cho tao qua").
4️⃣ Body (Nội dung thư)
Chỉ dùng cho POST/PUT. Là dữ liệu thực tế muốn gửi (như ví dụ JSON món ăn ở trên). GET và DELETE thường không có Body (vì xin xem hoặc xóa thì không cần gửi kèm dữ liệu gì to tát).
Giải phẫu API Response (Phản hồi)
Khi Server trả lời, chúng ta quan tâm nhất cái gì?
1️⃣ Status Code (Mã trạng thái) - Cực quan trọng
Server lười nói chuyện, nó chỉ giơ biển số lên:
-
2xx (Thành công - Màu xanh):
-
200 OK: Ổn áp. -
201 Created: Tạo mới thành công.
-
-
4xx (Lỗi tại mày/Client - Màu vàng/đỏ):
-
400 Bad Request: Gửi dữ liệu sai format. -
401 Unauthorized: Chưa đăng nhập. -
403 Forbidden: Đăng nhập rồi nhưng không có quyền (Nhân viên đòi vào trang Giám đốc). -
404 Not Found: Sai địa chỉ.
-
-
5xx (Lỗi tại tao/Server - Màu đỏ):
-
500 Internal Server Error: Code trên server bị bug, crash.
-
2️⃣ Response Body
Dữ liệu server trả về (thường là JSON) để Frontend lấy hiển thị lên màn hình.
🥊Phần 2: PUT vs PATCH: SỰ KHÁC BIỆT "CHẾT NGƯỜI"
Định nghĩa (Theo ngôn ngữ đời thường)
Hãy tưởng tượng bạn có một Ngôi Nhà (Resource) đã xây xong. Bây giờ bạn muốn thay đổi nó.
-
PUT (Replace - Thay thế toàn bộ):
-
Bạn đập tan ngôi nhà cũ đi.
-
Bạn xây lại một ngôi nhà mới hoàn toàn trên mảnh đất đó.
-
Hệ quả: Nếu ngôi nhà mới bạn quên vẽ cái cửa sổ, thì kết quả là nhà không có cửa sổ (Dù nhà cũ có).
-
-
PATCH (Modify - Sửa đổi một phần):
-
Bạn giữ nguyên ngôi nhà cũ.
-
Bạn chỉ trát lại xi măng chỗ bức tường bị nứt, hoặc sơn lại cái cổng.
-
Hệ quả: Mọi thứ khác (cửa sổ, mái ngói) vẫn y nguyên như cũ.
-
Ví dụ Code minh họa
Giả sử trong Database đang có một User như sau:
// Dữ liệu gốc (Original)
{
"id": 1,
"username": "anhtester",
"email": "anh@gmail.com",
"age": 30
}
Bây giờ, chúng ta muốn sửa age thành 31.
🔴 Cách dùng PUT (Nguy hiểm nếu không cẩn thận)
Nguyên tắc của PUT là: "Gửi cái gì thì lưu cái đó. Không gửi nghĩa là Xóa".
-
Request đúng: Phải gửi TOÀN BỘ thông tin (cả những cái không sửa).
// Gửi lên Server { "username": "anhtester", // Vẫn phải gửi lại "email": "anh@gmail.com", // Vẫn phải gửi lại "age": 31 // Cái cần sửa }=> Kết quả: User cập nhật thành công, đầy đủ thông tin.
-
Request sai (Tai nạn): Chỉ gửi mỗi cái cần sửa.
// Gửi lên Server { "age": 31 }=> Kết quả thảm họa:
{ "id": 1, "username": null, // 😱 MẤT MÀU! "email": null, // 😱 MẤT MÀU! "age": 31 }(Vì PUT hiểu là: "Mày đưa tao mỗi cái tuổi, tức là mày muốn user này chỉ có mỗi thuộc tính tuổi, mấy cái kia bỏ đi").
🟢 Cách dùng PATCH (An toàn, Tiết kiệm)
Nguyên tắc của PATCH là: "Gửi cái gì thì sửa cái đó. Không gửi thì giữ nguyên".
-
Request: Chỉ cần gửi đúng cái muốn sửa.
{ "age": 31 }=> Kết quả:
{ "id": 1, "username": "anhtester", // ✅ Vẫn còn nguyên "email": "anh@gmail.com", // ✅ Vẫn còn nguyên "age": 31 // ✅ Đã cập nhật }
Bảng so sánh chốt lại
| Đặc điểm | PUT (Thay thế) | PATCH (Vá) |
| Cơ chế | Xóa cũ -> Ghi đè mới hoàn toàn. | Tìm field tương ứng -> Cập nhật giá trị. |
| Dữ liệu gửi đi | Phải gửi Trọn bộ (Full Object). | Chỉ gửi Phần thay đổi (Partial Object). |
| Băng thông | Tốn hơn (vì gửi cục data to). | Tiết kiệm hơn (chỉ gửi data nhỏ). |
| Idempotency (Tính ổn định) | Có. Chạy PUT 100 lần với cùng 1 data thì kết quả vẫn y hệt nhau. | Không chắc. (Ví dụ PATCH lệnh "tăng tuổi thêm 1", chạy 100 lần thì tuổi tăng 100). |
🛠️ Phần 3: LÀM CHỦ CÔNG CỤ API & "PHÉP THUẬT" cURL
Trước khi dạy Robot (Playwright) chạy, con người (Tester) phải chạy được trước đã. Hôm nay chúng ta sẽ học cách dùng các "vũ khí" manual mạnh nhất với trang thực hành quốc dân: JSONPlaceholder.
Giới thiệu 2 "Đấu sĩ" 🥊
🧡 Postman (Ông Vua)
-
Là gì? Công cụ test API phổ biến nhất thế giới.
-
Ưu điểm: Tính năng tận răng, lưu lại lịch sử test lâu dài.
-
Nhược điểm: Cần cài đặt, hơi nặng máy.
💚 Hoppscotch (Kẻ thách thức - Web Based)
-
Là gì? Công cụ test API chạy ngay trên trình duyệt (trước đây là Postwoman).
-
Ưu điểm: Siêu nhẹ, không cần cài, giao diện giống hệt Postman.
-
Link: hoppscotch.io
Bí mật của các Senior: cURL là gì? 📜
cURL chính là Ngôn ngữ chung của API. Nó giống như một 'gói tin nén', chứa đầy đủ: URL, Method (GET/POST), Header, Body trong một đoạn văn bản duy nhất."
Tại sao cURL lại thần thánh?
-
Copy 1 phát là chạy: Bạn copy cURL từ máy Dev A, ném vào máy Dev B (hoặc Postman), nó chạy y hệt. Không lo sai lệch.
-
Debug siêu nhanh: Lấy cURL từ Network Tab (Browser) ném vào Postman để sửa lỗi.
Thực hành: Quy trình "Copy - Paste thần thánh" ⚡
Chúng ta sẽ thực hành quy trình làm việc thực tế: Lấy request từ web -> Đưa vào tool để mổ xẻ.
🟢 Bước 1: Lấy cURL từ Trình duyệt (Chrome/Edge)
-
Truy cập trang:
https://jsonplaceholder.typicode.com -
Bấm F12 -> Chuyển sang tab Network.
-
Trên giao diện web, kéo xuống tìm nút "Run script" (hoặc nút thử nghiệm API nào đó trên trang).
-
Mẹo: Nếu trang này tĩnh quá, hãy bảo học sinh F12 xong bấm F5 (Reload) trang chủ.
-
-
Trong tab Network, tìm request có tên là
1(hoặcposts). -
Chuột phải vào request đó -> Copy -> Copy as cURL (bash).
🟠 Bước 2: Import vào Postman
Thay vì ngồi gõ lại từng dòng Header, Body... ta làm như sau:
-
Mở Postman.
-
Bấm nút Import (Góc trên bên trái).
-
Dán đoạn cURL vừa copy vào ô trống.
-
Bấm Import.
-
✨ Bùm! Postman tự động điền hết URL:
https://jsonplaceholder.typicode.com/posts/1, Method GET. -
Bấm Send để thấy kết quả trả về.
🟣 Bước 3: Import vào Hoppscotch (Test nhanh)
Tương tự Postman:
-
Truy cập
hoppscotch.io. -
Bấm vào nút Import (Biểu tượng mũi tên đi xuống hoặc dòng chữ Import).
-
Chọn Import from cURL.
-
Dán vào -> Bấm Import.
-
Chạy ngon lành!
🤖 Phần 4: VIẾT CODE AUTO API ĐẦU TIÊN
Chuẩn bị môi trường (Setup) ⚙️
Đầu tiên vào file playwright.config.ts để đổi địa chỉ nhà (Base URL) sang trang thực hành mới.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
// 👇 Đổi sang địa chỉ này
baseURL: 'https://jsonplaceholder.typicode.com',
// Thu thập Trace để debug nếu lỗi
trace: 'on-first-retry',
},
});
🧩 GIẢI PHẪU: request FIXTURE LÀ CÁI QUÁI GÌ?
Trong Playwright, Fixture giống như "Hộp đồ nghề thần kỳ".
Mỗi khi bắt đầu một bài test (test(...)), Playwright sẽ phát cho bạn một hộp đồ nghề mới tinh. Bạn muốn dùng cái gì thì thò tay vào lấy cái đó ra (thông qua dấu { }).
-
Muốn điều khiển trình duyệt? 👉 Lấy
page. -
Muốn gọi API? 👉 Lấy
request. -
Muốn biết tên bài test? 👉 Lấy
testInfo.
Hình ảnh ví von: page vs. request 🚛 🏍️
Hãy tưởng tượng việc Gửi dữ liệu lên Server giống như việc Ship hàng.
🚛 page (Trình duyệt) = Xe Tải Hạng Nặng
-
Đặc điểm: Nó phải chở theo cả đống thứ: Hình ảnh, CSS, Font chữ, Quảng cáo, JavaScript chạy ngầm...
-
Quy trình: Phải nổ máy, mở cửa thùng xe, bốc xếp hàng, lái đi từ từ, đợi đèn đỏ (render UI).
-
Tốc độ: Chậm chạp, cồng kềnh.
-
Dùng khi: Bạn cần kiểm tra xem "Hàng hóa có được bọc đẹp không?" (Test giao diện).
🏍️ request (API Context) = Xe Máy Shipper (Ninja Lead)
-
Đặc điểm: Chỉ chở đúng Gói hàng (Data JSON). Không chở hình ảnh, không chở giao diện.
-
Quy trình: Vặn ga là bay. Luồn lách siêu nhanh. Đi đường tắt (Gửi thẳng đến Server).
-
Tốc độ: Tốc độ ánh sáng.
-
Dùng khi: Bạn chỉ cần quan tâm "Hàng có đến nơi an toàn không?" (Test Logic/Data).
👉 Kết luận: request fixture chính là chiếc xe máy đó. Nó được Playwright độ riêng để chạy API siêu tốc mà không cần bật cái trình duyệt nặng nề lên.
"Under the Hood" (Bên trong nó có gì?)
Khi bạn viết async ({ request }) => { ... }, Playwright âm thầm làm 3 việc này cho bạn:
✅ 1. Tự động "Học bài" từ Config
Nó sẽ đọc file playwright.config.ts.
-
Nếu bạn cài
baseURL: 'https://reqres.in', nó tự nhớ. Lúc code bạn chỉ cần gõ/api/users, nó tự ghép vào. -
Nếu bạn cài
extraHTTPHeaders(ví dụ Token), nó tự động đeo cái Token đó vào mọi request.
✅ 2. Tạo môi trường Độc lập (Isolation) - Cực quan trọng!
Đây là điểm "ăn tiền" so với dùng thư viện ngoài (như Axios).
-
Mỗi bài test = 1
requestmới tinh. -
Bài test 1 bạn đăng nhập (có Cookie A).
-
Bài test 2 Playwright sẽ hủy cái
requestcũ, tạo cái mới. Cookie A biến mất. -
👉 Tác dụng: Đảm bảo bài test này không làm ảnh hưởng bài test kia (No Flaky Tests).
✅ 3. Quản lý Cookies & Storage
Nó thông minh hơn curl. Nếu trong cùng 1 bài test, bạn gọi API Login thành công, server trả về Cookie. Thì các lệnh gọi API tiếp theo trong bài đó sẽ tự động mang theo Cookie đó.
Viết Code: CRUD (Create - Read - Update - Delete)
Tạo file mới: tests/api-jsonplaceholder.spec.ts.
Copy toàn bộ đoạn code mẫu "chuẩn chỉ" dưới đây vào.
🟢 Phần 1: GET - Lấy dữ liệu (Read)
import { test, expect } from '@playwright/test';
test('GET - Lấy thông tin bài viết số 1', async ({ request }) => {
// 1. Gửi yêu cầu GET đến endpoint /posts/1
// (Không cần gõ domain vì đã config baseURL rồi)
const response = await request.get('/posts/1');
// 2. Kiểm tra status code: Phải là 200 OK
expect(response.status()).toBe(200);
// 3. Phân tích cục JSON trả về
const body = await response.json();
console.log('GET Response:', body);
// 4. Kiểm tra nội dung (Assertion)
expect(body.id).toBe(1);
expect(body.userId).toBe(1);
// Kiểm tra tiêu đề có chứa chữ gì đó không
expect(body.title).toContain('sunt aut facere');
});
🟡 Phần 2: POST - Tạo mới (Create)
test('POST - Tạo bài viết mới', async ({ request }) => {
// 1. Gửi yêu cầu POST kèm theo Body (Data)
const response = await request.post('/posts', {
data: {
title: 'Học Playwright API',
body: 'Bài 3: Thực hành với JSONPlaceholder',
userId: 1
}
});
// 2. Kiểm tra status: 201 Created
expect(response.status()).toBe(201);
// 3. Kiểm tra dữ liệu trả về
const resJson = await response.json();
console.log('POST Response:', resJson);
// JSONPlaceholder luôn trả về id = 101 cho bài mới
expect(resJson.title).toBe('Học Playwright API');
expect(resJson.id).toBe(101);
});
🟠 Phần 3: PUT vs PATCH - Sửa đổi (Update)
test('PUT - Sửa toàn bộ (Thay thế)', async ({ request }) => {
// PUT bắt buộc gửi ĐỦ CÁC TRƯỜNG
const response = await request.put('/posts/1', {
data: {
id: 1,
title: 'Tiêu đề MỚI (PUT)',
body: 'Nội dung MỚI',
userId: 1
}
});
expect(response.status()).toBe(200);
const resJson = await response.json();
expect(resJson.title).toBe('Tiêu đề MỚI (PUT)');
});
test('PATCH - Sửa một phần (Vá)', async ({ request }) => {
// PATCH chỉ cần gửi cái muốn sửa
const response = await request.patch('/posts/1', {
data: {
title: 'Chỉ sửa mỗi cái Tiêu đề thôi (PATCH)'
}
});
expect(response.status()).toBe(200);
const resJson = await response.json();
// Verify: Tiêu đề đổi, nhưng body vẫn giữ nguyên
expect(resJson.title).toBe('Chỉ sửa mỗi cái Tiêu đề thôi (PATCH)');
});
🔴 Phần 4: DELETE - Xóa
test('DELETE - Xóa bài viết', async ({ request }) => {
const response = await request.delete('/posts/1');
// JSONPlaceholder trả về 200 khi xóa thành công (Nhiều nơi khác trả 204)
expect(response.status()).toBe(200);
// Verify: Body trả về thường là rỗng {}
const body = await response.json();
console.log('DELETE Response:', body);
expect(body).toEqual({});
});
Các kỹ thuật Assert (Kiểm tra) nâng cao 🧠
Kiểm tra kiểu dữ liệu:
expect(typeof body.id).toBe('number'); // ID phải là số, không được là chuỗi
expect(typeof body.title).toBe('string');
Kiểm tra mảng (Array):
const res = await request.get('/posts');
const list = await res.json();
// Kiểm tra danh sách trả về có đúng 100 bài không
expect(list.length).toBe(100);
// Kiểm tra phần tử đầu tiên trong mảng
expect(list[0]).toHaveProperty('userId', 1);
Soft Assertion (Kiểm tra mềm):
Dùng expect.soft nếu muốn: "Lỗi dòng này thì cứ chạy tiếp các dòng dưới, đừng dừng lại".
expect.soft(response.status()).toBe(200);
🩺Phần 4: SOFT ASSERTION - KHÁM TỔNG QUÁT
Vấn đề của expect thường (Hard Assertion)
Hãy tưởng tượng bạn đi khám sức khỏe với quy trình "Khắt khe" (Hard Assertion):
-
Bác sĩ kiểm tra mắt: Tốt.
-
Bác sĩ kiểm tra mũi: Bị viêm xoang! -> DỪNG LẠI! ĐUỔI VỀ LUÔN! ❌
Hậu quả: Bạn về nhà chữa xoang xong. Hôm sau đến khám tiếp.
3. Bác sĩ kiểm tra họng: Bị đau họng! -> DỪNG TIẾP! ĐUỔI VỀ! ❌
👉 Kết luận: Nếu dùng expect() thường, bạn phải chạy đi chạy lại Test rất nhiều lần mới sửa hết lỗi. Test chết ngay tại lỗi đầu tiên.
Giải pháp expect.soft (Soft Assertion)
Quy trình "Linh hoạt" (Soft Assertion):
-
Bác sĩ kiểm tra mắt: Tốt.
-
Bác sĩ kiểm tra mũi: Viêm xoang -> Bác sĩ ghi vào sổ, nhưng vẫn khám tiếp.
-
Bác sĩ kiểm tra họng: Đau họng -> Bác sĩ ghi vào sổ, khám tiếp.
-
... Khám hết toàn thân ...
-
KẾT LUẬN CUỐI CÙNG: "Bạn bị bệnh (Fail), danh sách bệnh gồm: Mũi, Họng..."
👉 Tác dụng: Chạy test MỘT LẦN DUY NHẤT, bắt được TOÀN BỘ LỖI.
Phân tích Code
test('Demo 10 Soft Assertions (So sánh với expect thường)', async ({ request }) => {
const response = await request.get('/posts/1');
const body = await response.json();
console.log('🏁 BẮT ĐẦU CHẠY 10 ASSERTIONS...');
// ✅ 1. Check ID (ĐÚNG)
expect.soft(body.id, { message: 'Assertion #1: id phải là 1' }).toBe(1);
// ✅ 2. Check UserId (ĐÚNG)
expect.soft(body.userId, { message: 'Assertion #2: userId phải là 1' }).toBe(1);
// ❌ 3. Check ID (CỐ TÌNH SAI)
expect.soft(body.id, { message: 'Assertion #3: id phải là 999 (CỐ TÌNH SAI)' }).toBe(999);
// ✅ 4. Check Title (ĐÚNG)
expect.soft(body.title, { message: 'Assertion #4: title chứa "sunt aut"' }).toContain('sunt aut');
// ❌ 5. Check Title (CỐ TÌNH SAI)
expect.soft(body.title, { message: 'Assertion #5: title chứa ABCXYZ (CỐ TÌNH SAI)' }).toContain('ABCXYZ');
// ✅ 6. Check Type (ĐÚNG)
expect.soft(typeof body.body, { message: 'Assertion #6: body phải là string' }).toBe('string');
// ❌ 7. Check UserId (CỐ TÌNH SAI)
expect.soft(body.userId, { message: 'Assertion #7: userId phải là 999 (CỐ TÌNH SAI)' }).toBe(999);
// ✅ 8. Check Property (ĐÚNG)
expect.soft(body, { message: 'Assertion #8: có thuộc tính title' }).toHaveProperty('title');
// ❌ 9. Check Property (CỐ TÌNH SAI)
expect.soft(body, { message: 'Assertion #9: có fakeProperty (CỐ TÌNH SAI)' }).toHaveProperty('fakeProperty');
// ✅ 10. Check GreaterThan (ĐÚNG)
expect.soft(body.id, { message: 'Assertion #10: id > 0' }).toBeGreaterThan(0);
console.log('🏁 Test đã chạy HẾT 10 assertions!');
console.log('📊 Nếu dùng expect() thường, test sẽ dừng ở lỗi #3');
console.log('📊 Với expect.soft(), bạn thấy được TẤT CẢ 4 lỗi: #3, #5, #7, #9');
});
Hãy cùng "mổ xẻ" luồng chạy của nó:
🛑 Nếu dùng expect() thường (Hard Assertion)
// Dòng này Fail (#3)
expect(body.id).toBe(999);
// 💀 CHƯƠNG TRÌNH CRASH TẠI ĐÂY!
// Các dòng dưới (#4, #5...) KHÔNG BAO GIỜ ĐƯỢC CHẠY.
// Console.log cuối cùng cũng không hiện ra.
✅ Khi dùng expect.soft()
Chương trình sẽ chạy băng băng từ đầu đến cuối như một chiếc xe tăng:
-
Assertion #1, #2: ✅ Pass (Êm ru).
-
Assertion #3: ❌ FAIL! (Nhưng Playwright chỉ đánh dấu "Có lỗi" rồi chạy tiếp).
-
Assertion #4: ✅ Pass.
-
Assertion #5: ❌ FAIL! (Lại ghi sổ).
-
... chạy tiếp đến #10 ...
-
Cuối cùng: In ra dòng
console.log('🏁 Test đã chạy HẾT...'). -
Báo cáo: Playwright đánh dấu Test Case này là FAILED và liệt kê danh sách 4 tội đồ: #3, #5, #7, #9.
Khi nào NÊN và KHÔNG NÊN dùng?
Không phải lúc nào cũng dùng soft đâu nhé.
👍 NÊN DÙNG (Cho API & Form Data)
Khi các dữ liệu độc lập với nhau.
-
Ví dụ: Kiểm tra Response API User.
-
Tên đúng không?
-
Email đúng không?
-
Tuổi đúng không?
-
👉 Nếu tên sai thì cứ kiểm tra tiếp email. Đừng dừng lại. Code của bạn chính là trường hợp này.
-
👎 KHÔNG NÊN DÙNG (Cho Critical Flow)
Khi bước sau phụ thuộc vào bước trước.
-
Ví dụ:
-
API Login (
expecttoken trả về). -
API Lấy thông tin User (Dùng token trên).
-
👉 Nếu bước 1 (Login) đã Fail rồi, thì bước 2 chạy làm gì nữa cho tốn thời gian? Lúc này bắt buộc phải dùng
expectthường để chặn ngay.
-
5. Kết quả hiển thị trong Report
Khi chạy đoạn code trên, màn hình Terminal hoặc HTML Report sẽ trông rất "hoành tráng" như sau:
Error:
1. Assertion #3: id phải là 999 (CỐ TÌNH SAI)
Expected: 999
Received: 1
2. Assertion #5: title chứa ABCXYZ (CỐ TÌNH SAI)
Expected substring: "ABCXYZ"
Received string: "sunt aut facere..."
3. Assertion #7: userId phải là 999 (CỐ TÌNH SAI)
Expected: 999
Received: 1
4. Assertion #9: có fakeProperty (CỐ TÌNH SAI)
Expected: object to have property "fakeProperty"
👉 Giá trị: nhìn vào đây phát là biết ngay API đang hỏng ở những chỗ nào, sửa một lèo là xong.
