paint-brush
Kiểm thử trong Java: Các khái niệm chính [Phần 1: Kiểm thử đơn vị]từ tác giả@gromspys
1,621 lượt đọc
1,621 lượt đọc

Kiểm thử trong Java: Các khái niệm chính [Phần 1: Kiểm thử đơn vị]

từ tác giả Sergei Korneev10m2023/11/14
Read on Terminal Reader

dài quá đọc không nổi

Trong bài viết này, chúng ta sẽ khám phá các khái niệm chính về thử nghiệm đơn vị Java, bao gồm các ví dụ mã đơn giản, tham số hóa, thử nghiệm ngoại lệ và chú thích.
featured image - Kiểm thử trong Java: Các khái niệm chính [Phần 1: Kiểm thử đơn vị]
Sergei Korneev HackerNoon profile picture

Kiểm thử đơn vị là một phương pháp cơ bản trong phát triển phần mềm nhằm đảm bảo độ tin cậy và tính chính xác của mã của bạn. Trong bài viết này, chúng ta sẽ khám phá các khái niệm chính về kiểm thử đơn vị Java, bao gồm các ví dụ mã đơn giản, tham số hóa, kiểm tra ngoại lệ, các chú thích như @Before , @BeforeEach , @After@AfterEach , cũng như cách sử dụng mô hình và sơ khai .


Chúng tôi cũng sẽ giới thiệu thư viện Mockito phổ biến để mô phỏng và cung cấp hướng dẫn về cách đo mức độ bao phủ của mã bằng cách sử dụng plugin Maven.

Tầm quan trọng của việc kiểm tra đơn vị

Kiểm thử đơn vị là thực hành kiểm thử các thành phần hoặc đơn vị mã riêng lẻ để xác minh tính chính xác của chúng. Mục tiêu chính là phát hiện và sửa lỗi sớm trong quá trình phát triển, đảm bảo rằng mỗi đơn vị mã hoạt động như mong đợi.


Hãy bắt đầu bằng cách tạo một lớp máy tính đơn giản mà chúng ta có thể sử dụng cho các ví dụ của mình. Lớp Calculator chứa các phép tính số học cơ bản:

 public class Calculator { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { return a / b; } }

Viết một bài kiểm tra đơn vị đơn giản

Để tạo một bài kiểm tra đơn vị cho lớp Calculator của chúng tôi, bạn có thể sử dụng khung JUnit, một khung kiểm tra phổ biến cho Java. Hãy viết một bài kiểm tra đơn vị đơn giản cho phương thức add :

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

Trong thử nghiệm này, chúng tôi nhập các lớp JUnit cần thiết và chú thích phương thức thử nghiệm bằng @Test . Sau đó, chúng tôi tạo một thể hiện của lớp Calculator và xác nhận rằng kết quả của phương thức add bằng giá trị mong đợi (5).

Kiểm tra tham số

Kiểm tra tham số hóa cho phép bạn chạy cùng một logic kiểm tra với nhiều bộ dữ liệu đầu vào. Điều này rất hữu ích khi thử nghiệm một phương thức có nhiều giá trị đầu vào khác nhau. Để thực hiện việc này trong JUnit, bạn có thể sử dụng chú thích @ParameterizedTest và cung cấp các giá trị đầu vào cũng như kết quả mong đợi dưới dạng tham số. Đây là một ví dụ:

 import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { @ParameterizedTest @CsvSource({"2, 3, 5", "4, 7, 11", "0, 0, 0"}) public void testAdd(int a, int b, int expected) { Calculator calculator = new Calculator(); int result = calculator.add(a, b); assertEquals(expected, result); } }

Trong ví dụ này, chú thích @ParameterizedTest cho phép chúng tôi cung cấp nhiều bộ giá trị đầu vào và kết quả mong đợi ở định dạng CSV. Phương pháp thử nghiệm được thực thi cho từng kết hợp, đảm bảo tính chính xác của phương thức add .

Kiểm tra ngoại lệ

Kiểm tra các ngoại lệ là rất quan trọng để đảm bảo mã của bạn xử lý lỗi một cách thích hợp. Để kiểm tra các trường hợp ngoại lệ, bạn có thể sử dụng chú thích @Test cùng với phương thức assertThrows do JUnit cung cấp.


Đây là một ví dụ:

 javaCopy codeimport org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; public class CalculatorTest { @Test public void testDivideByZero() { Calculator calculator = new Calculator(); assertThrows(ArithmeticException.class, () -> calculator.divide(5, 0)); } }

Trong thử nghiệm này, chúng tôi xác minh rằng việc chia một số cho 0 trong lớp Calculator sẽ tạo ra ArithmeticException .

Sử dụng chú thích: @Before, @BeforeEach, @After và @AfterEach

Các chú thích như @Before , @BeforeEach , @After@AfterEach được sử dụng để thiết lập và phá bỏ môi trường thử nghiệm. Các chú thích này giúp quản lý các tác vụ khởi tạo và dọn dẹp thử nghiệm phổ biến.


  • @Before@After lần lượt chạy một lần trước và sau tất cả các phương thức kiểm tra trong lớp kiểm tra.
  • @BeforeEach@AfterEach chạy trước và sau mỗi phương thức kiểm tra.


Đây là một ví dụ:

 import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { private Calculator calculator; @BeforeEach public void setUp() { calculator = new Calculator(); } @AfterEach public void tearDown() { calculator = null; } @Test public void testAdd() { int result = calculator.add(2, 3); assertEquals(5, result); } @Test public void testSubtract() { int result = calculator.subtract(5, 3); assertEquals(2, result); } }

Trong ví dụ này, phương thức setUp được chú thích bằng @BeforeEach và nó khởi tạo đối tượng Calculator trước mỗi lần kiểm tra. Phương thức tearDown , được chú thích bằng @AfterEach , sẽ dọn sạch tài nguyên sau mỗi lần kiểm tra.

Mô phỏng và sơ khai

Mô phỏng và sơ khai rất cần thiết trong kiểm thử đơn vị khi bạn muốn tách biệt mã đang được kiểm thử khỏi các phần phụ thuộc bên ngoài, chẳng hạn như cơ sở dữ liệu hoặc dịch vụ web. Chúng cho phép bạn mô phỏng hành vi của những phần phụ thuộc này.


  • Mock: Mock là một đối tượng bắt chước hành vi của một đối tượng thật nhưng nằm trong tầm kiểm soát của bạn. Nó ghi lại các tương tác và cho phép bạn xác minh rằng các phương thức cụ thể được gọi.


  • Sơ khai: Sơ khai cung cấp các phản hồi soạn sẵn cho các lệnh gọi phương thức. Nó trả về dữ liệu được xác định trước và được sử dụng để mô phỏng các hành vi nhất định của hệ thống bên ngoài.


Hãy mở rộng lớp Calculator để bao gồm một phương thức liên quan đến phần phụ thuộc bên ngoài và sau đó tạo một phần sơ khai để mô phỏng phần phụ thuộc đó.

 public class Calculator { private ExternalService externalService; public Calculator(ExternalService externalService) { this.externalService = externalService; } public int performComplexCalculation(int a, int b) { int result = externalService.multiply(a, b); return result + externalService.getConstant(); } }

Trong lớp Calculator được cập nhật này, performComplexCalculation dựa vào một ExternalService để nhân và thu được một giá trị không đổi.


Đây là cách bạn có thể tạo một sơ khai để kiểm tra lớp Calculator mà không phụ thuộc vào ExternalService thực tế.

 import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; public class CalculatorTest { @Test public void testPerformComplexCalculation() { // Create a stub for ExternalService ExternalService externalServiceStub = new ExternalService() { @Override public int multiply(int a, int b) { return a * b; } @Override public int getConstant() { return 5; // Stubbed constant value } }; // Create the Calculator with the stubbed ExternalService Calculator calculator = new Calculator(externalServiceStub); // Test the performComplexCalculation method int result = calculator.performComplexCalculation(2, 3); assertEquals(11, result); // 2*3 + 5 (constant) = 11 } }

Trong thử nghiệm này, ExternalService được đặt trong phương thức thử nghiệm bằng cách tạo một lớp ẩn danh ghi đè các phương thức cần thiết. Bằng cách này, phương pháp kiểm tra Calculator sẽ chạy mà không phụ thuộc vào việc triển khai thực tế của ExternalService .


Sơ khai rất hữu ích cho việc mô phỏng hành vi của các hệ thống bên ngoài hoặc các phần phụ thuộc để tách biệt và kiểm tra chức năng cụ thể của một lớp hoặc phương thức. Điều này cho phép bạn kiểm soát hành vi của phần sơ khai và tập trung vào đơn vị đang được thử nghiệm mà không cần các dịch vụ thực tế bên ngoài.

Giới thiệu Mockito cho Mocking

Mockito là một thư viện Java phổ biến để tạo và quản lý các đối tượng giả. Giả sử chúng ta có lớp PaymentService tương tác với cổng thanh toán bên ngoài. Chúng ta có thể sử dụng Mockito để tạo mô hình cho cổng thanh toán:

 import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; public class PaymentServiceTest { @Test public void testProcessPayment() { PaymentGateway paymentGateway = mock(PaymentGateway.class); PaymentService paymentService = new PaymentService(paymentGateway); when(paymentGateway.charge(100.0)).thenReturn(true); boolean result = paymentService.processPayment(100.0); assertTrue(result); verify(paymentGateway, times(1)).charge(100.0); } }

Trong ví dụ này, chúng tôi tạo một PaymentGateway mô phỏng và sử dụng phương thức when để xác định hành vi của nó. Sau đó, chúng tôi gọi phương thức processPayment trên lớp PaymentService và xác minh rằng phương thức charge đã được gọi với các tham số dự kiến.

Bảo hiểm mã và Plugin Maven

Phạm vi mã đo lường tỷ lệ phần trăm dòng mã, nhánh hoặc câu lệnh được thực thi bởi các bài kiểm tra đơn vị của bạn. Nó giúp bạn xác định mã chưa được kiểm tra và các khu vực có thể yêu cầu kiểm tra bổ sung.


Maven là một công cụ xây dựng phổ biến cho các dự án Java và bạn có thể tích hợp phân tích phạm vi mã vào dự án Maven của mình bằng cách sử dụng các plugin như JaCoCo. Đây là cách thêm JaCoCo vào dự án của bạn:


  1. Mở tệp pom.xml của dự án của bạn.
  2. Thêm cấu hình plugin JaCoCo Maven:
 <build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.7</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins> </build>


Chạy bản dựng Maven của bạn bằng lệnh sau:

 mvn clean verify


Sau khi chạy bản dựng, bạn có thể tìm thấy báo cáo phạm vi mã trong tệp target/site/jacoco/index.html . Báo cáo này cung cấp thông tin chi tiết về mức độ bao phủ mã của bạn bằng các bài kiểm tra đơn vị.

Phần kết luận

Kiểm thử đơn vị Java là một phương pháp thiết yếu để đảm bảo độ tin cậy và tính chính xác của mã của bạn. Với các công cụ như JUnit và Mockito, bạn có thể viết các bài kiểm tra đơn vị và mô phỏng hiệu quả cho các thành phần của mình.


Bằng cách tích hợp phân tích phạm vi mã với Maven và JaCoCo, bạn có thể đảm bảo rằng các thử nghiệm của bạn bao gồm một phần đáng kể cơ sở mã của bạn. Thường xuyên kiểm tra và duy trì các bài kiểm tra đơn vị sẽ giúp bạn tạo ra các ứng dụng Java mạnh mẽ và chất lượng cao.


Trong phần tiếp theo của loạt bài này, chúng ta sẽ khám phá thử nghiệm tích hợp, một khía cạnh quan trọng của thử nghiệm phần mềm liên quan đến việc thử nghiệm sự tương tác giữa các thành phần và dịch vụ khác nhau để đảm bảo hệ thống tổng thể hoạt động chính xác.


Hãy theo dõi phần hai, nơi chúng ta sẽ đi sâu vào thế giới thử nghiệm tích hợp thú vị!