CodeGym /Blog Java /Ngẫu nhiên /Kiểm tra đơn vị trong Java với JUnit

Kiểm tra đơn vị trong Java với JUnit

Xuất bản trong nhóm

Thử nghiệm đơn vị trong Java là gì?

Trước khi chúng ta bắt đầu tìm hiểu về JUnit trong Java, hãy tìm hiểu sơ qua về kiểm thử đơn vị là gì và tại sao nó lại phổ biến như vậy (nếu bạn đã biết nội dung này rồi, hãy bỏ qua phần 'Làm cách nào để viết Kiểm thử JUnit trong Java?'). Thử nghiệm đơn vị trong Java giúp cho việc phát triển phần mềm quy mô lớn trở nên hiệu quả và dễ dàng hơn nhiều. Nó có thể giúp cả cá nhân và nhóm cắt giảm vô số thời gian gỡ lỗi và hợp lý hóa quá trình cộng tác. Kiểm tra đơn vị trong Java với JUnit - 1

https://junit.org/junit4/

Ý tưởng cơ bản của thử nghiệm đơn vị là: viết các thử nghiệm nguyên tử của các tính năng riêng lẻ (được gọi là thử nghiệm đơn vị) và từ từ thêm nhiều tính năng hơn sau khi thử nghiệm và đảm bảo các tính năng trước đó hoạt động. Đó là một ý tưởng cực kỳ đơn giản nhưng mạnh mẽ. Để làm ví dụ về quy trình này, hãy tưởng tượng bạn đang xây dựng một máy tính khoa học ảo. Ngoài các toán tử số học rõ ràng ( +, -, x, %), máy tính này sẽ có các tính năng nâng cao yêu cầu các tính năng phụ khác hoạt động bên trong nó. Để tính số mũ, máy tính của bạn cần có khả năng nhân chính xác. Vì vậy, một phương pháp thử nghiệm đơn vị để xây dựng và thử nghiệm máy tính này sẽ là:
  • Viết hàm cộng. Kiểm tra nó cẩn thận, thay đổi nó, lặp lại cho đến khi nó hoạt động.
  • Thực hiện tương tự cho các phép tính trừ, nhân, chia.
  • Sử dụng các toán tử cơ sở này để viết các hàm toán tử nâng cao hơn như số mũ, sau đó cũng kiểm tra các hàm đó.
Điều này đảm bảo rằng các tính năng xây dựng từ các tính năng phụ nhỏ hơn khác không chỉ hoạt động bình thường theo đúng nghĩa của chúng mà còn không có các tính năng phụ bị lỗi bên trong nó. Ví dụ: nếu tôi đang kiểm tra hàm số mũ và có gì đó không ổn, tôi biết rằng lỗi đó có thể không nằm ở tính năng con của phép nhân, vì hàm nhân đã được thử nghiệm rộng rãi. Điều này giúp loại bỏ đáng kể tổng số lượng mã mà tôi cần phải truy nguyên và kiểm tra để tìm ra lỗi. Hy vọng rằng, ví dụ tầm thường này làm rõ cách cấu trúc quá trình suy nghĩ xung quanh Kiểm tra đơn vị. Nhưng làm thế nào để kiểm thử đơn vị tương tác với phần còn lại của quy trình phát triển phần mềm? Điều gì sẽ xảy ra nếu bạn có các tính năng thậm chí phức tạp hơn, cần có khả năng hoạt động và giao tiếp cùng nhau? Thử nghiệm đơn vị là không đủ để đảm bảo rằng các tính năng phức tạp như vậy có thể hoạt động bình thường cùng nhau. Trên thực tế, đây chỉ là bước đầu tiên của Bốn cấp độ kiểm thử phần mềm (Tôi sử dụng chữ in hoa vì tôi đề cập đến tiêu chuẩn ngành hoặc cách tiếp cận phổ biến nhất để kiểm thử phần mềm). Ba bước cuối cùng làKiểm thử tích hợp , kiểm thử hệ thốngkiểm thử chấp nhận. Tất cả những điều này có thể có nghĩa chính xác như những gì bạn nghĩ, nhưng hãy để tôi làm rõ: Thử nghiệm tích hợp là những gì chúng tôi sẽ làm để đảm bảo những thứ như đã đề cập ở trên, “các tính năng phức tạp”, tương tác đúng cách với nhau. (ví dụ: đảm bảo máy tính có thể xử lý “3 + 7 * 4 - 2”) Thử nghiệm hệ thống là thử nghiệm thiết kế tổng thể của một hệ thống cụ thể; thường có nhiều hệ thống gồm các tính năng phức tạp hoạt động cùng nhau trong một sản phẩm, vì vậy bạn nhóm chúng thành các hệ thống và kiểm tra chúng riêng lẻ. (ví dụ: nếu bạn đang xây dựng một máy tính vẽ đồ thị, thì trước tiên bạn phải xây dựng 'hệ thống' số học để xử lý các con số, kiểm tra cho đến khi nó hoạt động như dự định, sau đó bạn sẽ xây dựng và kiểm tra 'hệ thống' vẽ đồ thị để xử lý việc rút tiền, như nó sẽ được xây dựng từ hệ thống số học). Thử nghiệm chấp nhận là thử nghiệm ở cấp độ người dùng; nó đang xem liệu tất cả các hệ thống có thể hoạt động đồng bộ để tạo ra một sản phẩm hoàn chỉnh sẵn sàng được người dùng chấp nhận hay không (ví dụ: người dùng kiểm tra máy tính). Các nhà phát triển phần mềm đôi khi có thể bỏ qua bước cuối cùng này của quy trình, vì các công ty thường sẽ nhờ các nhân viên khác triển khai các thử nghiệm người dùng (beta) một cách riêng biệt.

Làm cách nào để viết bài kiểm tra JUnit trong Java?

Bây giờ bạn đã có một ý tưởng rõ ràng hơn về những lợi ích và hạn chế của thử nghiệm đơn vị, hãy xem qua một số đoạn mã! Chúng ta sẽ sử dụng một khung kiểm tra Java phổ biến có tên là JUnit (một khung phổ biến khác là TestNG, bạn cũng có thể sử dụng nếu muốn. Chúng rất giống nhau về mặt cú pháp; TestNG được lấy cảm hứng từ JUnit). Bạn có thể tải xuống và cài đặt JUnit tại đây . Đối với mã ví dụ này, chúng tôi sẽ tiếp tục ví dụ về 'máy tính khoa học' mà tôi đã đề cập trước đó; nó khá đơn giản để hiểu và mã kiểm tra cực kỳ dễ dàng. Thực tiễn thông thường là viết các lớp kiểm tra riêng biệt cho từng lớp của bạn, vì vậy đó là những gì chúng tôi sẽ làm. Giả sử rằng tại thời điểm này, chúng ta có một Math.javatệp chứa tất cả các hàm toán học trong đó (bao gồm cả Math.add) và chúng ta đang viết mộtMathTests.javatập tin trong cùng một gói. Bây giờ, hãy thiết lập các câu lệnh nhập và nội dung lớp: (CÂU HỎI PHỎNG VẤN JUnit CÓ THỂ: Bạn có thể được hỏi nơi đặt bài kiểm tra JUnit của mình và liệu bạn có cần nhập các tệp nguồn của mình hay không. Nếu bạn đang viết các lớp kiểm tra của mình trong cùng một gói với các lớp chính của bạn, thì bạn không cần bất kỳ câu lệnh nhập nào cho các tệp nguồn của mình trong lớp kiểm tra. Nếu không, hãy đảm bảo rằng bạn đang nhập các tệp nguồn của mình!)

import org.junit.jupiter.Test;    //gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; //less typing :) 

public class MathTests {
	//...
}
Câu lệnh nhập đầu tiên cung cấp cho chúng tôi @Testtiêu đề. Chúng tôi viết ' @Test' trực tiếp trên mỗi định nghĩa hàm kiểm tra, để JUnit biết rằng đây là một bài kiểm tra đơn vị đơn lẻ có thể chạy riêng. Sau này, tôi sẽ chỉ cho bạn cách bạn có thể chạy thử nghiệm đơn vị cụ thể bằng tiêu đề này. Câu lệnh nhập thứ hai giúp chúng ta tiết kiệm một chút thời gian gõ. Hàm JUnit chính mà chúng tôi sử dụng để kiểm tra các hàm của mình là to Assert.assertEquals(), hàm này nhận hai tham số (giá trị thực và giá trị dự kiến) và đảm bảo chúng bằng nhau. Việc có câu lệnh nhập thứ hai này cho phép chúng ta chỉ cần gõ ' assertEquals(...' thay vì phải chỉ định mỗi lần nó là một phần của gói nào. Bây giờ hãy viết một trường hợp kiểm tra rất đơn giản để xác minh rằng 2 + 2 thực sự là 4!

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test
	public void add_twoPlusTwo_returnsFour(){
	final int expected = 4;
	final int actual = Math.add(2, 2);
	assertEquals(“2+2 is 4”, actual, expected);
	}
}
Hãy xem qua năm dòng của từng chức năng kiểm tra và chức năng của chúng: Dòng 5: @TestTiêu đề này xác định rằng định nghĩa chức năng bên dưới add_twoPlusTwo_returnsFour()thực sự là một chức năng kiểm tra mà JUnit có thể chạy riêng. Dòng 6: Đây là chữ ký hàm cho trường hợp thử nghiệm của chúng tôi. Các trường hợp thử nghiệm luôn rất đơn lẻ; họ chỉ kiểm tra một ví dụ cụ thể, chẳng hạn như 2+2=4. Quy ước đặt tên cho các trường hợp thử nghiệm của bạn ở dạng “ [function]_[params]_returns[expected](),” trong đó [function]là tên của hàm bạn đang thử nghiệm, [params]là các tham số ví dụ cụ thể mà bạn đang thử nghiệm và [expected]là giá trị trả về dự kiến ​​của hàm. Các chức năng kiểm tra hầu như luôn có kiểu trả về là ' void' vì điểm chính của toàn bộ chức năng là chạyassertEquals, sẽ xuất ra bảng điều khiển cho dù bài kiểm tra của bạn có vượt qua hay không; bạn không cần bất kỳ dữ liệu nào khác được trả về ở bất kỳ đâu. Dòng 7: Chúng tôi khai báo một finalbiến ' ' có kiểu trả về là Math.add (int), và đặt tên nó là 'mong đợi' theo quy ước. Giá trị của nó là câu trả lời mà chúng ta đang mong đợi (4). Dòng 8: Chúng tôi khai báo một finalbiến ' ' có kiểu trả về là Math.add (int), và đặt tên nó là 'thực tế' theo quy ước. Giá trị của nó là kết quả của Math.add(2, 2). Dòng 9: Dòng vàng. Đây là dòng so sánh thực tế và dự kiến ​​và cho chúng tôi biết chúng tôi chỉ vượt qua bài kiểm tra nếu chúng bằng nhau. Tham số đầu tiên được truyền “2+2 là 4” là mô tả về chức năng kiểm tra.

Điều gì xảy ra nếu chức năng của tôi đưa ra một ngoại lệ?

Nếu ví dụ thử nghiệm cụ thể của bạn đưa ra một ngoại lệ thay vì khẳng định rằng giá trị thực tế và giá trị mong đợi là bằng nhau, thì JUnit có cách làm rõ điều này trong tiêu đề @Test. Hãy cùng xem một ví dụ dưới đây. Giả sử chúng ta có một hàm trong Math.javagọi là Math.divide, chúng ta muốn đảm bảo rằng đầu vào không thể chia cho 0. Thay vào đó, cố gắng gọi Math.divide(a, 0)bất kỳ giá trị 'a' nào sẽ đưa ra một ngoại lệ ( ArithmeticException.class). Chúng tôi chỉ định như vậy trong tiêu đề như sau:

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test (expectedExceptions = ArithmeticException.class)
	public void divide_byZero_throwsException() throws ArithmeticException{
	Math.divide(1, 0);
	}
}
Bạn có thể có nhiều ngoại lệ cho expectedExceptions, chỉ cần đảm bảo sử dụng dấu ngoặc và dấu phẩy để liệt kê các lớp ngoại lệ của bạn, chẳng hạn như:

expectedException = {FirstException.class, SecondException.class, … }

Làm cách nào để chạy thử nghiệm JUnit của tôi trong Java?

Cách thêm JUnit vào IntelliJ: https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea Bạn có thể chạy dự án của mình như bình thường khi chạy thử nghiệm. Chạy tất cả các bài kiểm tra trong một lớp kiểm tra sẽ chạy chúng theo thứ tự bảng chữ cái. Trong JUnit 5, bạn có thể thêm mức độ ưu tiên cho các bài kiểm tra bằng cách thêm @Orderthẻ. Một ví dụ:

@TestMethodOrder(OrderAnnotation.class)
public class Tests {
…
@Test
@Order(2)
public void a_test() { … }

@Test
@Order (1)
public void b_test() { … }
…
}
Mặc dù a_test()đứng trước b_test()theo thứ tự bảng chữ cái và trong mã, b_test()sẽ chạy trước a_test()ở đây, vì 1 đứng trước 2 theo thứ tự. Vì vậy, đó là tất cả những điều cơ bản về JUnit. Bây giờ, hãy giải quyết một số Câu hỏi Phỏng vấn JUnit phổ biến và tìm hiểu thêm về JUnit trong quá trình thực hiện!

Câu hỏi phỏng vấn JUnit (Thông tin bổ sung)

Ở đây tôi đã thu thập các câu hỏi phỏng vấn JUnit phổ biến nhất. Nếu bạn có điều gì đó để thêm - vui lòng làm điều này trong phần bình luận bên dưới. Hỏi: Bạn có thể gọi phương thức nào trong phương thức kiểm tra của mình để tự động trượt kiểm tra? A: fail(“mô tả lỗi ở đây!”); Q: Bạn đang thử nghiệm một lớp Dog; để kiểm tra đối tượng Dog, bạn cần khởi tạo đối tượng đó trước khi có thể chạy kiểm tra đối tượng đó. Vì vậy, bạn viết một hàm setUp() để khởi tạo Dog. Bạn chỉ muốn chạy chức năng này một lần trong suốt quá trình thử nghiệm. Bạn phải đặt gì ngay phía trên chữ ký hàm setUp() để JUnit biết chạy setUp() trước khi chạy thử nghiệm? Đ: @B BeforeClass (@B BeforeAll trong JUnit 5) Hỏi:Chữ ký hàm của hàm setUp() được mô tả ở trên phải là gì? A: khoảng trống tĩnh công khai. Bất kỳ hàm nào có @B BeforeClass (@B BeforeAll trong JUnit 5) hoặc @afterClass (@afterAll trong JUnit 5) đều cần ở trạng thái tĩnh. Q: Bạn đã kiểm tra xong lớp Dog. Bạn viết hàm voidearDown() để dọn sạch dữ liệu và in thông tin ra bàn điều khiển sau mỗi lần kiểm tra. Bạn muốn chức năng này chạy sau mỗi lần kiểm tra. Bạn phải đặt gì ngay phía trên chữ ký của hàmearDown() để JUnit biết chạyearDown() sau khi chạy mỗi bài kiểm tra? Đ: @after (@afterEach trong JUnit 5)
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION