Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

API Automation — REST Assured (Java)

Mở đầu — vì sao bài này quan trọng

Nếu bạn đang làm QA cho một hệ thống backend viết bằng Java — chẳng hạn một ngân hàng số, một sàn thương mại điện tử, hay một startup fintech ở TP.HCM — thì gần như chắc chắn team của bạn sẽ hỏi: "API test viết bằng gì để chạy chung với code backend?". Câu trả lời phổ biến nhất trong thế giới Java chính là REST Assured.

REST Assured là một thư viện Java mã nguồn mở chuyên để kiểm thử API REST. Điều làm nó nổi bật không phải là tính năng — mà là cách viết. REST Assured cho phép bạn diễn đạt một test theo cấu trúc Given-When-Then (BDD-style), đọc gần như tiếng Anh tự nhiên: "Cho một request có header X, Khi gọi GET đến endpoint Y, Thì status code phải là 200 và trường Z phải bằng...". Một người không phải lập trình viên đọc vào vẫn hiểu được ý định của test.

Vì sao bài này quan trọng với sự nghiệp của bạn? Vì trong các doanh nghiệp lớn tại Việt Nam — VNG, Tiki, MoMo, các ngân hàng như Techcombank, VPBank — backend thường là Java/Spring Boot. Khi API test được viết cùng ngôn ngữ với backend, chúng có thể nằm chung repository, chạy chung Maven/Gradle build, và dev có thể tự đọc, tự sửa test khi họ đổi API. Một SDET biết REST Assured trở thành cầu nối tự nhiên giữa team QA và team Dev. Bài này sẽ trang bị cho bạn đúng kỹ năng đó: từ setup Maven, viết test đầu tiên, đến những kỹ thuật thực chiến như xác thực JSON, authentication, và trích xuất dữ liệu để nối chuỗi các request.

Lưu ý phạm vi: bài này tập trung riêng vào REST Assured trong Java. Các cách tiếp cận khác cho API automation — như Python requests + pytest, hay Postman/Newman cho người không code — được nói ở các bài riêng. Ở đây chúng ta đi sâu vào thế giới Java.

Khái niệm cốt lõi

REST Assured là gì và nằm ở đâu trong bức tranh test

REST Assured là một Java DSL (Domain-Specific Language) — tức một "ngôn ngữ chuyên biệt" được xây trên nền Java để mô tả các thao tác kiểm thử HTTP một cách gọn gàng. Bạn không cần tự viết code mở kết nối HTTP, serialize JSON, parse response — REST Assured lo hết. Việc của bạn là mô tả ý định: gọi cái gì, gửi gì, và kỳ vọng gì.

Nó thao tác ở tầng API/service — dưới UI, trên unit. Nghĩa là bạn kiểm thử hợp đồng (contract) của một endpoint: gửi request đúng thì có nhận response đúng không, status code, body, header có như thỏa thuận không. Đây là loại test nhanh, ổn định và rẻ để bảo trì hơn nhiều so với test qua trình duyệt.

Cú pháp Given-When-Then

Trái tim của REST Assured là ba từ khóa:

  • given() — thiết lập điều kiện đầu vào: header, query param, path param, body, authentication.
  • when() — hành động: phương thức HTTP (GET, POST, PUT, DELETE...) và endpoint.
  • then() — kỳ vọng: kiểm tra status code, body, header, thời gian phản hồi.
Một test tối giản:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Test public void layThongTinNguoiDung() { given() .baseUri("https://api.example.vn") .header("Accept", "application/json") .when() .get("/users/42") .then() .statusCode(200) .body("name", equalTo("Nguyen Van A")) .body("email", containsString("@")); }

Đọc lên gần như một câu tiếng Anh. Phần body("name", ...) dùng cú pháp GPath (giống JsonPath) để trỏ vào trường trong JSON response, và equalTo, containsString là các Hamcrest matcher để diễn đạt điều kiện.

Setup với Maven

REST Assured được thêm vào project qua Maven. Trong pom.xml:

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <version>5.4.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.2</version>
    <scope>test</scope>
</dependency>

scope=test nghĩa là thư viện chỉ dùng khi chạy test, không đi kèm vào bản build production. REST Assured đã bao gồm sẵn Hamcrest và JsonPath, nên thường bạn không cần khai báo thêm. Nếu dùng Gradle thì tương đương là testImplementation 'io.rest-assured:rest-assured:5.4.0'.

Gửi và nhận JSON body

Với POST/PUT, bạn thường gửi một JSON body. REST Assured cho phép truyền chuỗi trực tiếp, hoặc — tốt hơn — truyền một object Java (POJO) và để nó tự serialize:

given()
    .contentType("application/json")
    .body("{ \"email\": \"a@shop.vn\", \"amount\": 150000 }")
.when()
    .post("/orders")
.then()
    .statusCode(201)
    .body("orderId", notNullValue());

Authentication

Đa số API thực tế cần xác thực. REST Assured hỗ trợ nhiều kiểu: Basic Auth, OAuth2, và phổ biến nhất hiện nay là Bearer token:

given()
    .header("Authorization", "Bearer " + accessToken)
.when()
    .get("/me")
.then()
    .statusCode(200);

Trích xuất dữ liệu từ response

Rất nhiều kịch bản cần lấy giá trị từ response này để dùng cho request kế tiếp — ví dụ: login lấy token, rồi dùng token đó gọi API khác. Đây là lúc dùng extract():

String token =
    given()
        .contentType("application/json")
        .body("{ \"username\": \"qa\", \"password\": \"123456\" }")
    .when()
        .post("/auth/login")
    .then()
        .statusCode(200)
        .extract().path("data.accessToken");

Biến token giờ có thể dùng cho các request tiếp theo. Đây là kỹ thuật cốt lõi để dựng một luồng test end-to-end ở tầng API.

Tình huống thực tế

Tình huống 1: Sàn TMĐT kiểm thử API giỏ hàng trước mỗi lần release

Một công ty thương mại điện tử tầm trung ở Hà Nội (giả định gọi là "ChoViet") có backend Spring Boot với khoảng 120 endpoint. Trước đây họ test API thủ công bằng Postman, mỗi lần release mất gần một ngày công của 2 tester để "click qua" các luồng chính. Sau mỗi hotfix, không ai dám khẳng định các API khác còn nguyên vẹn.

Team quyết định viết bộ regression API bằng REST Assured, đặt chung repo với backend. Họ dựng khoảng 80 test cho các luồng quan trọng: đăng nhập, thêm sản phẩm vào giỏ, áp mã giảm giá, tạo đơn. Một test tiêu biểu cho luồng áp mã giảm giá:

@Test
public void apMaGiamGia_giamDungGiaTri() {
    given()
        .header("Authorization", "Bearer " + token)
        .contentType("application/json")
        .body("{ \"cartId\": \"C123\", \"coupon\": \"SALE10\" }")
    .when()
        .post("/cart/apply-coupon")
    .then()
        .statusCode(200)
        .body("discountPercent", equalTo(10))
        .body("finalPrice", lessThan(originalPrice));
}

Diễn giải: Vì test nằm chung repo, mỗi khi dev đổi cấu trúc response của /cart/apply-coupon, build sẽ đỏ ngay và họ tự sửa được. Thời gian regression giảm từ gần một ngày công xuống còn 4 phút chạy tự động.

Bài học: Sức mạnh lớn nhất của REST Assured không nằm ở cú pháp, mà ở chỗ nó nói cùng ngôn ngữ với backend — biến API test thành tài sản chung của cả Dev lẫn QA, thay vì "việc riêng của tester".

Tình huống 2: Fintech kiểm thử luồng đăng nhập nối chuỗi token

Một startup fintech ở Singapore phục vụ thị trường Đông Nam Á có API yêu cầu Bearer token cho mọi endpoint nghiệp vụ. Tester mới vào viết test kiểu "hardcode token dán tay vào code" — được vài hôm token hết hạn, cả bộ test đỏ lòm, mất buổi sáng để tìm nguyên nhân.

Giải pháp là để REST Assured tự lấy token trong bước setup, rồi truyền cho các test sau:

@BeforeAll
static void dangNhapLayToken() {
    token =
        given()
            .contentType("application/json")
            .body("{ \"username\": \"qa_bot\", \"password\": \"" + System.getenv("QA_PASS") + "\" }")
        .when()
            .post("/auth/login")
        .then()
            .statusCode(200)
            .extract().path("data.accessToken");
}

Mật khẩu được đọc từ biến môi trường QA_PASS, không hardcode trong code — tránh lộ credential khi push lên Git.

Bài học: Trong hệ thống có auth, đừng bao giờ hardcode token. Hãy dùng extract() để lấy token động và @BeforeAll để lấy một lần dùng chung. Và tuyệt đối tách secret ra biến môi trường — đây là chuẩn mực bảo mật cơ bản mà nhiều tester Việt Nam hay quên.

Tình huống 3: Ngân hàng dùng RequestSpecification để tránh lặp code

Một ngân hàng số tại TP.HCM có gần 300 API test REST Assured. Ban đầu mỗi test đều lặp lại baseUri, header Accept, Authorization, contentType... khiến file dài dòng và khi domain đổi từ staging sang UAT phải sửa hàng trăm chỗ.

Họ tái cấu trúc bằng RequestSpecification — một "khuôn" cấu hình dùng chung:

RequestSpecification spec = new RequestSpecBuilder()
    .setBaseUri(System.getProperty("baseUri", "https://uat.bank.vn"))
    .addHeader("Authorization", "Bearer " + token)
    .setContentType("application/json")
    .build();

// Mỗi test giờ gọn hơn nhiều: given().spec(spec) .when().get("/accounts/balance") .then().statusCode(200).body("currency", equalTo("VND"));

Bài học: Khi số lượng test lớn lên, hãy tách phần cấu hình lặp lại ra RequestSpecification (và ResponseSpecification cho phần kiểm tra chung). Đổi môi trường chỉ cần đổi một biến -DbaseUri=... khi chạy Maven. Đây chính là khác biệt giữa một bộ test "chạy được" và một bộ test "bảo trì được lâu dài".

Hướng dẫn từng bước

Sau đây là quy trình dựng bộ API test REST Assured đầu tiên của bạn.

Bước 1 — Tạo project Maven. Dùng mvn archetype:generate hoặc IDE (IntelliJ IDEA) tạo project Java chuẩn. Đảm bảo có thư mục src/test/java.

Bước 2 — Thêm dependency. Bổ sung rest-assuredjunit-jupiter vào pom.xml như phần trên, cùng maven-surefire-plugin để mvn test nhận diện được JUnit 5.

Bước 3 — Viết test "smoke" đầu tiên. Chọn một endpoint GET công khai không cần auth (ví dụ endpoint health-check). Kiểm tra statusCode(200). Chạy mvn test để chắc chắn hạ tầng hoạt động trước khi đi sâu.

Bước 4 — Cấu hình cấu hình chung. Đặt baseUri, basePath mặc định trong một lớp base test hoặc @BeforeAll. Nếu có nhiều môi trường, đọc từ system property hoặc biến môi trường.

Bước 5 — Xử lý authentication. Viết bước login lấy token, lưu vào biến static, dùng cho các test cần auth. Đọc credential từ biến môi trường.

Bước 6 — Viết assertion có ý nghĩa. Đừng chỉ kiểm statusCode. Hãy kiểm cả nội dung: body("field", equalTo(...)), kiểm kiểu dữ liệu, kiểm mảng (body("items.size()", greaterThan(0))), kiểm thời gian phản hồi (time(lessThan(2000L))).

Bước 7 — Nhóm và đặt tên rõ ràng. Đặt tên method test theo nghiệp vụ (taoDonHang_thanhCong, taoDonHang_thieuEmail_tra400). Test không chỉ để chạy — nó là tài liệu sống mô tả hành vi API.

Bước 8 — Tái cấu trúc khi lớn dần. Khi có vài chục test, tách RequestSpecification/ResponseSpecification, tách POJO cho request/response body, và tách phần login ra helper riêng.

Lỗi thường gặp & mẹo

Nhầm static import. REST Assured phụ thuộc nhiều vào static import: import static io.restassured.RestAssured.;import static org.hamcrest.Matchers.;. Thiếu chúng, given()equalTo() sẽ báo lỗi không tìm thấy. Đây là lỗi số một của người mới.

GPath sai đường dẫn. Nếu response là { "data": { "user": { "name": "A" } } } thì path đúng là data.user.name, không phải user.name. Khi test đỏ vì "không tìm thấy trường", hãy in cả response ra để soi cấu trúc: thêm .log().all() vào then(), hoặc .log().ifValidationFails() để chỉ in khi test fail.

Hardcode token, baseUri, secret. Như đã nói ở tình huống 2 và 3 — đây là mầm mống của test giòn và rủi ro lộ credential. Luôn dùng biến môi trường và RequestSpecification.

Kiểu số trong JSON. JSON không phân biệt int/long rõ như Java. equalTo(150000) có thể fail nếu REST Assured parse ra kiểu khác. Mẹo: dùng matcher linh hoạt như equalTo(150000) với chú ý kiểu, hoặc extract() rồi so sánh thủ công khi cần chính xác.

Chỉ kiểm status code. Một test chỉ kiểm statusCode(200) gần như vô dụng — API có thể trả 200 với body rỗng hoặc sai. Luôn kiểm cả nội dung.

Mẹo — bật logging thông minh. given().log().all() in toàn bộ request, hữu ích khi debug. Nhưng trong CI, dùng .log().ifValidationFails() để log gọn, chỉ xuất khi có lỗi.

Mẹo — dùng JsonSchema validation. Với API quan trọng, thêm rest-assured module json-schema-validator và dùng body(matchesJsonSchemaInClasspath("order-schema.json")) để kiểm toàn bộ cấu trúc response cùng lúc, thay vì kiểm từng trường.

Bài tập thực hành

Dùng một API công khai để luyện tay (ví dụ https://reqres.in hoặc một mock server nội bộ), hãy hoàn thành:

  • Setup: Tạo project Maven, thêm REST Assured + JUnit 5, viết test GET đầu tiên kiểm statusCode(200) và chạy mvn test thành công.
  • Assertion nội dung: Viết test GET danh sách người dùng, kiểm rằng mảng có ít nhất 1 phần tử và trường email của phần tử đầu chứa ký tự @.
  • POST + extract: Viết test tạo mới một resource bằng POST, kiểm statusCode(201), và dùng extract().path("id") để lấy id trả về, in ra console.
  • Nối chuỗi: Dùng id vừa lấy để gọi GET /resource/{id} và kiểm dữ liệu khớp với những gì đã tạo.
  • Tái cấu trúc: Gom baseUri và header chung vào một RequestSpecification, viết lại cả bốn test trên để dùng chung spec.
  • Nâng cao: Thêm một test kiểm case lỗi — gửi body thiếu trường bắt buộc và khẳng định API trả 400 cùng thông báo lỗi phù hợp.
Làm xong 6 bài này, bạn đã nắm đủ nền tảng để đóng góp API test thật cho một project Java.

Tóm tắt

REST Assured là lựa chọn hàng đầu để tự động hóa kiểm thử API REST trong hệ sinh thái Java. Sức mạnh của nó đến từ cú pháp Given-When-Then dễ đọc, khả năng tích hợp chung repo với backend Spring Boot, và bộ công cụ đầy đủ cho xác thực, trích xuất dữ liệu và kiểm tra response.

Những điểm cần khắc cốt: cấu hình qua Maven với scope=test; nhớ static import; dùng GPath để trỏ vào JSON và Hamcrest matcher để assert; xử lý auth bằng token động qua extract(); và khi bộ test lớn lên, tách cấu hình chung ra RequestSpecification để dễ bảo trì và đổi môi trường. Đừng bao giờ hardcode secret, và đừng bao giờ chỉ kiểm mỗi status code.

Ba tình huống thực tế — sàn TMĐT rút regression từ một ngày còn 4 phút, fintech quản lý token động an toàn, ngân hàng chuẩn hóa bằng spec dùng chung — cho thấy REST Assured không chỉ là công cụ viết test, mà là cách biến chất lượng API thành tài sản chung của cả team. Khi bạn thành thạo nó, bạn không còn là "người click Postman" mà trở thành SDET thực thụ, viết code chất lượng ngang hàng với đội backend.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi