Giới thiệu chung

Hiện tại cũng có khá nhiều loại testing được sử dụng, tuy nhiên mỗi loại testing lại có cách thức sử dụng và trường hợp sử dụng khác nhau, tuy nhiên mình sẽ chỉ đi tìm hiểu một vài loại hình testing phổ biến, cũng như mình thường sử dụng trong quá trình phát triển sản phẩm.

Các bạn có thể xem qua ảnh để có được cái nhìn tổng quan:

Các loại Testing

Unit Testing

Unit Testing hay còn gọi là Unit Test, hoặc văn phong hơn thì là kiểm thử đơn vị. Đây là loại kiểm thử tập trung vào việc kiểm tra từng đơn vị (như hàm, phương thức…) của chương trình.

Mục đích của kiểm thử đơn vị là đảm bảo rằng mỗi đơn vị hoạt động đúng như mong đợi. Unit Test là một trong những phương pháp kiểm thử quan trọng để đảm bảo chất lượng phần mềm và giảm thiểu sự cố hệ thống.

Thường một vài công ty bé bé, hoặc như hồi xưa mình làm thì cũng thường chỉ dừng ở Unit test, kết hợp thêm với việc tự kiểm tra của bản thân trong những phần logic.

Không phải hoàn toàn nhưng mà sẽ có trường hợp xảy ra việc bị lọt bug (logic code, logic business, corner case,…) mà trong quá trình coding chưa lường trước được hết.

Các bước để thực hiện Unit Test

  • Bước đầu tiên là xác định phạm vi của Unit test, nó có thể bao gồm các phương thức, hàm hoặc lớp.
  • Các test case được viết để kiểm tra các chức năng của các đoạn mã, mỗi test case sẽ kiểm tra một phương thức, hàm hoặc lớp riêng lẻ và sẽ có một đầu ra mong đợi.
  • Để thực hiện Unit test, chúng ta sử dụng các Framework Test như JUnit, NUnit,… để thực thi các test case.
  • Sau khi thực hiện Unit test, chúng ta phải kiểm tra kết quả để đảm bảo rằng các đoạn mã hoạt động chính xác như dự định.

Lợi ích của việc sử dụng Unit Test

  • Unit test giúp đảm bảo rằng các đoạn mã được phát triển hoạt động chính xác và đúng như mong đợi của developer.
  • Giúp phát hiện sớm các lỗi và vấn đề trong phần mềm, từ đó giúp giảm thiểu sự cố hệ thống.
  • Các test case có thể được sử dụng để kiểm tra các phần mềm trong quá trình bảo trì và mở rộng, giúp đảm bảo rằng các thay đổi không ảnh hưởng đến các chức năng hiện tại của phần mềm.

Hạn chế của Unit Test

Tuy nhiên trong một vài dự án nhất định thì Unit test cũng có một vài hạn chế như là:

  • Unit test chỉ kiểm tra các đơn vị nhỏ nhất của phần mềm, nên nếu phần mềm có độ phức tạp cao, các unit test sẽ phải được viết để kiểm tra các đơn vị này một cách kỹ lưỡng và đầy đủ, tốn nhiều thời gian và công sức.
  • Unit test không thể kiểm tra được các tương tác giữa các đơn vị của phần mềm, bên cung cấp dịch vụ thứ 3 và do đó không đảm bảo tính tương thích của các đơn vị này khi kết hợp với nhau.
  • Unit test chỉ kiểm tra các yếu tố có chạy mã, nhưng không thể kiểm tra các yếu tố không chạy mã như thiết kế giao diện người dùng, tính di động, bảo mật, v.v.
  • Unit test không thể đảm bảo tính toàn vẹn của dữ liệu, và do đó không thể kiểm tra các lỗi liên quan đến quản lý và bảo vệ dữ liệu.
  • Việc viết và chạy unit test đòi hỏi nhiều thời gian và công sức của nhà phát triển, điều này tăng chi phí cho quá trình phát triển phần mềm.

Trong thời gian đầu của dự án, nếu quá tập trung tỉ mỉ vào việc viết Unit Test sẽ dễ xảy đến trường hợp dự án trễ deadline, không kịp tiến độ, vậy nên các kĩ sư cần xác định một mức độ viết Unit Test vừa phải, phù hợp với từng giai đoạn của dự án.

Ví dụ một vài Unit Test

import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class CalculatorTest {
 
    @Test
    public void testAdd() {
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        assertEquals(5, result);
    }
}
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class PrimeNumberTest {
 
    @Test
    public void testIsPrime() {
        PrimeNumber primeNumber = new PrimeNumber();
        boolean result = primeNumber.isPrime(5);
        assertEquals(true, result);
    }
}

Integration Testing

Integration testing hay còn gọi là kiểm thử tích hợp, đây là loại kiểm thử tập trung vào việc kiểm tra tích hợp giữa các thành phần của chương trình (như module, hệ thống con, third party,…).

Mục đích của kiểm thử tích hợp là đảm bảo rằng các thành phần của chương trình tương tác với nhau một cách đúng đắn.

Đây là loại test mình sử dụng chiếm phần lớn, bởi vì mỗi khi viết code xong thì luôn đảm bảo các test case 100% đều được cover bằng Integration test.

Lợi ích của việc sử dụng Integration test trong khi viết code thì rất nhiều:

  1. Đảm bảo tính toàn vẹn của hệ thống: Integration test cho phép kiểm tra các thành phần phần mềm kết hợp với nhau xem có hoạt động đúng đắn hay không, đồng thời đưa ra cơ hội để khắc phục các lỗi kết nối giữa các thành phần trước khi triển khai.
  2. Tiết kiệm thời gian và chi phí: Integration test cho phép kiểm tra nhiều thành phần phần mềm cùng lúc, giúp tiết kiệm thời gian và chi phí so với việc kiểm tra từng thành phần riêng lẻ.
  3. Integration test giúp đảm bảo rằng hệ thống hoạt động đúng đắn khi kết hợp các thành phần với nhau, giúp làm tăng độ tin cậy của hệ thống và giảm thiểu rủi ro cho người dùng cuối.
  4. Giúp phát hiện lỗi sớm: Integration test giúp phát hiện các lỗi kết nối giữa các thành phần phần mềm sớm hơn, hoặc sự ảnh hưởng của những phần code mới tác động vào những phần code cũ, giúp giảm thiểu thời gian và chi phí để khắc phục các lỗi sau này.

Tuy nhiên với một vài dự án thời gian triển khai gấp và thay đổi yêu cầu/scope vào cuối sprint, thì mình thường viết vào sau quá trình development một chút, tức là trước khi bàn giao sang QC và các bên khác tích hợp thì mình sẽ luôn đảm bảo self-test cũng như cover đầy đủ hết các test case trước, và bước cuối là mới viết bổ sung Integration test.

Bên dưới sẽ là ví dụ về một số integration test cho API RESTful sử dụng Java và thư viện Spring Boot:

Test API endpoint trả về dữ liệu đúng dưới định dạng JSON:

@Test
public void testGetAllUsers() throws Exception {
    mockMvc.perform(get("/users"))
           .andExpect(status().isOk())
           .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
           .andExpect(jsonPath("$", hasSize(2)))
           .andExpect(jsonPath("$[0].id", is(1)))
           .andExpect(jsonPath("$[0].name", is("John")))
           .andExpect(jsonPath("$[1].id", is(2)))
           .andExpect(jsonPath("$[1].name", is("Jane")));
}

Test API endpoint trả về lỗi khi request với tham số không hợp lệ:

@Test
public void testGetUserByIdWithInvalidId() throws Exception {
    mockMvc.perform(get("/users/{id}", "invalid"))
           .andExpect(status().isBadRequest())
           .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
           .andExpect(jsonPath("$.message", is("Invalid user ID format")));
}

Test API endpoint thêm mới một user:

@Test
public void testAddUser() throws Exception {
    User user = new User("John");
    String userJson = objectMapper.writeValueAsString(user);
    
    mockMvc.perform(post("/users")
            .contentType(MediaType.APPLICATION_JSON_UTF8)
            .content(userJson))
            .andExpect(status().isCreated())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
            .andExpect(jsonPath("$.id", is(3)))
            .andExpect(jsonPath("$.name", is("John")));
}

Trong các ví dụ trên, chúng ta sử dụng Spring Boot Test để cấu hình và chạy các integration test, sử dụng thư viện Mockito để mock các dependency của API và sử dụng thư viện MockMvc để kiểm tra kết quả trả về của API.

Ngoài ra nhiều công ty sẽ dựa vào việc phân chia loại service khác nhau mà có những quy chuẩn, quy tắc viết Integration test khác nhau. Tuy nhiên vẫn phải tuân theo ba bước trong kịch bản kiểm thử: Given -> When -> Then.

  • Given: Mô tả trạng thái ban đầu của hệ thống trước khi thực hiện một hành động.
  • When: Mô tả hành động được thực hiện trên hệ thống.
  • Then: Mô tả kết quả mong đợi sau khi thực hiện hành động.

Smoke Testing

Smoke Test là một loại kiểm thử phần mềm cơ bản và nhanh để kiểm tra các tính năng chính của phần mềm sau khi sản phẩm đã được release lên production, smoke test được thực hiện như một phần của quy trình kiểm thử mực đích để đảm bảo rằng phần mềm đã được triển khai đúng và hoạt động ổn định như mong muốn.

Việc thực hiện smoke test được tiến hành trong khoảng vài giờ hoặc ngắn hơn, nếu xảy ra vấn đề nghiêm trọng không như mong muốn thì cần phải được xử lý ngay lập tức.

Mình lại nhớ đến cái hồi đội PO, Squad Lead vs QC phối hợp vào smoke test phần tích hợp Booking với chủ đầu tư Masterise, đợt đó phải đi tiền thật thì phải, nghĩ cũng hài.

Regression Testing

Regression testing (kiểm thử hồi quy) là một loại kiểm thử phần mềm nhằm đảm bảo rằng các thay đổi mới trong phần mềm không làm ảnh hưởng đến các tính năng đã hoạt động tốt trước đó.

Regression testing thường được thực hiện khi đã có sự thay đổi trong phần mềm, chẳng hạn như khi thêm tính năng mới, sửa đổi hoặc nâng cấp phần mềm. Nó có thể được thực hiện trên toàn bộ phần mềm hoặc chỉ trên một phần cụ thể của sản phẩm.

Regression testing thường bao gồm các bước sau:

  1. Chọn ra các ca kiểm thử đã được thực hiện trước đó để đảm bảo rằng chúng vẫn hoạt động đúng sau khi có thay đổi.
  2. Thiết lập môi trường kiểm thử để đảm bảo rằng việc kiểm thử sẽ được thực hiện trong một môi trường tương tự với môi trường sử dụng thực tế.
  3. Thực hiện các ca kiểm thử đã được lựa chọn trên môi trường kiểm thử.
  4. Phân tích kết quả kiểm thử để xác định xem liệu có sự thay đổi nào ảnh hưởng đến các tính năng đã hoạt động tốt trước đó hay không.
  5. Báo cáo các kết quả kiểm thử để có thể giải quyết các vấn đề phát hiện ra và cải thiện quy trình kiểm thử.

Regression testing giúp đảm bảo rằng các thay đổi mới không gây ra lỗi hoặc ảnh hưởng đến các tính năng đã hoạt động tốt trước đó. Tuy nhiên, việc thực hiện regression testing đòi hỏi thời gian và nguồn lực, và có thể trở nên tốn kém nếu phần mềm phức tạp hoặc có nhiều tính năng.

Tùy theo việc xác định phạm vi cần regression test mà QC có thể lựa chọn phạm vi cho phù hợp với tiến độ chung cũng như đảm bảo được chất lượng sản phẩm.

E2E Testing

End-to-End testing là một loại kiểm thử phần mềm nhằm đảm bảo rằng các tính năng và hành vi của toàn bộ hệ thống đều hoạt động đúng.

Nó tập trung vào việc kiểm tra tính tương thích giữa các thành phần khác nhau của hệ thống, bao gồm cả giao diện người dùng, hệ thống cơ sở dữ liệu, các API và các dịch vụ web.

E2E testing thường được thực hiện từ đầu đến cuối, bao gồm tất cả các ca sử dụng quan trọng mà người dùng có thể gặp phải. Thường nó sẽ đi theo một kịch bản(scenario) nhất định, và người viết test E2E sẽ viết theo scenario.

Điều này sẽ giúp đảm bảo rằng hệ thống hoạt động tốt trong môi trường thực tế và đáp ứng đầy đủ các yêu cầu của người dùng.

Một số lợi ích của E2E Testing

Những lợi ích của E2E testing bao gồm:

  • Kiểm tra tính đúng đắn và hoàn thiện của toàn bộ hệ thống.
  • Phát hiện sớm các lỗi có thể xảy ra khi các thành phần của hệ thống được tích hợp với nhau.
  • Tăng cường niềm tin của người dùng đối với hệ thống.

Hạn chế của E2E testing

Tuy nhiên E2E testing cũng có một số hạn chế nhất định, như là:

  • Việc thực hiện và triển khai hạ tầng E2E testing thời gian đầu tốn nhiều thời gian, sau thời gian này thì mọi thứ chuyển sang việc triển khai code.
  • Nhân sự viết E2E cần có kinh nghiệm nhất định về code, để có thể viết, trace bug, cũng như triển khai các kịch bản test thông qua code.
  • Việc tìm kiếm lỗi khá phức tạp và mất nhiều thời gian, do là tương tác với nhiều bên khác nhau.
  • Việc kiểm thử toàn bộ hệ thống có thể không thực tế trong một số trường hợp.

Hiện tại mình vẫn đang support QC ở công ty viết E2E, đó như là một yêu cầu bắt buộc trong công việc, giúp được cho QC có thể tự viết, tự debug và làm hoàn chỉnh những test case theo scenario của họ thì cũng là thành công cho chính mình.

Xem thêm một số bài viết nổi bật