Cách sử dụng JUnit trong Java | Trở thành lập trình viên

JUnit là một khung công tác được tiêu chuẩn hóa để kiểm tra các đơn vị Java (nghĩa là các lớp Java). JUnit có thể được tự động hóa để đưa một số công việc ra khỏi thử nghiệm.

Hãy tưởng tượng bạn đã tạo ra một loại enum với ba giá trị: XANH , VÀNG và ĐỎ . Liệt kê 1 chứa mã:

Listing 1

public enum SignalColor {
GREEN, YELLOW, RED
}

Đèn giao thông có trạng thái (là một tên ưa thích cho màu của đèn giao thông).

public class TrafficLight {
SignalColor state = SignalColor.RED;

Nếu bạn biết trạng thái hiện tại của đèn giao thông, bạn có thể quyết định trạng thái tiếp theo của đèn giao thông sẽ là gì.

public void nextState() {
switch (state) {
case RED:
state = SignalColor.GREEN;
break;
case YELLOW:
state = SignalColor.RED;
break;
case GREEN:
state = SignalColor.YELLOW;
break;
default:
state = SignalColor.RED;
break;
}
}

Bạn cũng có thể thay đổi trạng thái của đèn giao thông một số lần nhất định:

public void change(int numberOfTimes) {
for (int i = 0; i < numberOfTimes; i++) {
nextState();
}
}

Đặt tất cả lại với nhau, bạn có lớp TrafficLight trong Listing 2.

Listing 2

public class TrafficLight {
SignalColor state = SignalColor.RED;
public void nextState() {
switch (state) {
case RED:
state = SignalColor.GREEN;
break;
case YELLOW:
state = SignalColor.RED;
break;
case GREEN:
state = SignalColor.YELLOW;
break;
default:
state = SignalColor.RED;
break;
}
}
public void change(int numberOfTimes) {
for (int i = 0; i < numberOfTimes; i++) {
nextState();
}
}
}

Vào thời xa xưa, bạn có thể đã tiếp tục viết mã, tạo thêm các lớp, gọi nextState và thay đổi các phương thức trong Listing2. Sau đó, sau vài tháng mã hóa, bạn sẽ tạm dừng để kiểm tra công việc của mình.

Và thật bất ngờ! Bài kiểm tra của bạn sẽ thất bại thảm hại! Bạn không bao giờ nên trì hoãn việc kiểm tra trong hơn một hoặc hai ngày. Kiểm tra sớm và kiểm tra thường xuyên!

Một triết lý về kiểm thử nói rằng bạn nên kiểm tra từng đoạn mã ngay khi bạn viết nó. Tất nhiên, cụm từ chunk mã của Code không nghe rất khoa học. Sẽ không có gì khi các nhà phát triển đi bộ xung quanh nói về việc thử nghiệm mã chunk mã hóa mà họ đã làm vào chiều nay. Tốt hơn là gọi mỗi đoạn mã là một đơn vị và khiến các nhà phát triển nói về thử nghiệm đơn vị .

Đơn vị phổ biến nhất để thử nghiệm là một lớp. Vì vậy, một nhà phát triển Java điển hình kiểm tra từng lớp ngay khi mã của lớp được viết.

Vì vậy, làm thế nào để bạn đi kiểm tra một lớp học? Một người mới có thể kiểm tra lớp TrafficLight bằng cách viết một lớp bổ sung – một lớp có chứa một phương thức chính . Các chính phương pháp tạo ra một thể hiện của TrafficLight , gọi nextState và thay đổi phương pháp, và hiển thị kết quả. Người mới kiểm tra kết quả và so sánh chúng với một số giá trị dự kiến.

Sau khi viết các phương thức chính
cho hàng chục, hàng trăm hoặc thậm chí hàng nghìn lớp, người mới (hiện
là nhà phát triển chính thức) trở nên mệt mỏi với thói quen kiểm tra và
tìm cách tự động hóa quy trình kiểm tra. Mệt mỏi hay không, một nhà phát triển có thể cố gắng giải mã các bài kiểm tra mã hóa của nhà phát triển khác. Không
có bất kỳ tiêu chuẩn hoặc hướng dẫn nào để xây dựng các bài kiểm tra,
việc đọc và hiểu các bài kiểm tra của nhà phát triển khác có thể khó
khăn và tẻ nhạt.

Vì vậy, JUnit đến là 1 giải pháp cho chúng ta!.

Để tìm hiểu cách Eclipse tự động hóa việc sử dụng JUnit, hãy làm như sau:

1. Tạo một dự án Eclipse chứa Listing 1 và 2.

2. Trong Windows, bấm chuột phải vào nhánh TrafficLight.java của Gói thám hiểm. Trên máy Mac, bấm điều khiển vào nhánh TrafficLight.java của Gói thám hiểm.

Một menu ngữ cảnh xuất hiện.

3. Trong menu ngữ cảnh, chọn Mới → Trường hợp kiểm tra JUnit.

Kết quả là, hộp thoại JUnit Test Case mới xuất hiện.

4. Bấm Next ở dưới cùng của hộp thoại JUnit Test Case mới.

Kết quả là, bạn thấy trang thứ hai của hộp thoại Trường hợp kiểm tra JUnit mới. Trang thứ hai liệt kê các phương thức thuộc (trực tiếp hoặc gián tiếp) vào lớp TrafficLight .

5. Đặt một dấu kiểm trong hộp kiểm có nhãn Traffic Light.

Kết quả là, Eclipse đặt các dấu kiểm trong các hộp kiểm nextState ()thay đổi (int) .

6. Nhấp vào Kết thúc ở dưới cùng của hộp thoại Trường hợp kiểm tra JUnit mới.

JUnit không chính thức là một phần của Java. Thay vào đó đi kèm với tập hợp các lớp và phương thức riêng để giúp bạn tạo các bài kiểm tra cho mã của mình. Sau khi bạn nhấp vào Kết thúc, Eclipse sẽ hỏi bạn nếu bạn muốn bao gồm các lớp và phương thức JUnit như một phần của dự án của bạn.

7. Chọn Thực hiện hành động sau và thêm thư viện JUnit 4 vào đường dẫn xây dựng. Sau đó bấm OK.

Eclipse đóng các hộp thoại và dự án của bạn có tệp TrafficLightTest.java hoàn toàn mới . Mã của tệp được hiển thị trong Liệt kê 3.

Mã trong Liệt kê 3 chứa hai thử nghiệm và cả hai thử nghiệm đều chứa các lệnh gọi đến một phương thức thất bại nghe có vẻ khó chịu . Eclipse muốn bạn thêm mã để thực hiện các thử nghiệm này.

8. Loại bỏ các cuộc gọi đến phương thức thất bại. Thay cho các lệnh gọi phương thức fail, hãy nhập mã được in đậm trong Liệt kê 4.

9. Trong Gói Explorer, nhấp chuột phải (trong Windows) hoặc nhấp chuột điều khiển (trên máy Mac), nhánh TrafficLightTest.java. Trong menu ngữ cảnh kết quả, chọn Run As → JUnit Test.

Eclipse có thể có nhiều hơn một loại khung thử nghiệm JUnit trong tay áo của nó. Nếu vậy, bạn có thể thấy một hộp thoại như bên dưới. Nếu bạn làm như vậy, hãy chọn Trình khởi chạy JUnit của Eclipse và sau đó bấm OK.

Kết quả là, Eclipse chạy chương trình TrafficLightTest.java của bạn . Eclipse hiển thị kết quả của việc chạy trước Gói Explorer riêng của nó. Kết quả cho thấy không có lỗi và không có lỗi.

Listing 3

import static org.junit.Assert.*;
import org.junit.Test;
public class TrafficLightTest {
@Test
public void testNextState() {
fail("Not yet implemented");
}
@Test
public void testChange() {
fail("Not yet implemented");
}
}

Listing 4

import static org.junit.Assert.*;
import org.junit.Test;
public class TrafficLightTest {
@Test
public void testNextState() {
TrafficLight light = new TrafficLight();
assertEquals(SignalColor.RED, light.state);
light.nextState();
assertEquals(SignalColor.GREEN, light.state);
light.nextState();
assertEquals(SignalColor.YELLOW, light.state);
light.nextState();
assertEquals(SignalColor.RED, light.state);
}
@Test
public void testChange() {
TrafficLight light = new TrafficLight();
light.change(5);
assertEquals(SignalColor.YELLOW, light.state);
}
}

Khi bạn chọn Run As → JUnit Test, Eclipse không tìm kiếm một phương thức chính. Thay vào đó, Eclipse tìm kiếm các phương thức bắt đầu bằng @Test và những thứ khác. Eclipse thực thi từng phương thức @Test .

Những thứ như @Test là các chú thích Java .

Liệt kê 4 chứa hai phương thức @Test : testNextState và testChange . Các testNextState phương pháp đặt TrafficLight nextState phương pháp để thử nghiệm. Tương tự, phương thức testChange uốn cong các cơ của phương thức thay đổi TrafficLight .

Hãy xem xét mã trong phương thức testNextState . Các testNextState phương pháp lặp đi lặp lại gọi là TrafficLight lớp của nextState phương pháp và JUnit của assertEquals phương pháp. Các assertEquals phương pháp có hai tham số: một giá trị kỳ vọng và giá trị thực tế.

  • Trong lệnh gọi assertEquals trên cùng , giá trị mong đợi là SignalColor.RED . Bạn mong đợi đèn giao thông là ĐỎ bởi vì, trong Liệt kê 2, bạn khởi tạo trạng thái của đèn với giá trị SignalColor.RED .
  • Trong lệnh gọi assertEquals trên cùng , giá trị thực tế là light.state (màu thực sự được lưu trữ trong biến trạng thái của đèn giao thông ).

Nếu giá trị thực bằng với giá trị mong đợi, lệnh gọi tới assertEquals sẽ vượt qua và JUnit tiếp tục thực thi các câu lệnh trong phương thức testNextState .

Nhưng nếu giá trị thực tế khác với giá trị mong đợi, thì assertEquals không thành công và kết quả của lần chạy sẽ hiển thị lỗi. Ví dụ, hãy xem xét những gì xảy ra khi bạn thay đổi giá trị mong đợi trong lệnh gọi assertEquals đầu tiên trong Listing 4:

@Test
public void testNextState() {
TrafficLight light = new TrafficLight();
assertEquals(SignalColor.YELLOW, light.state);

Ngay sau khi xây dựng, màu đèn giao thông là ĐỎ , không phải VÀNG . Vì vậy, phương thức testNextState chứa một xác nhận sai và kết quả của việc thực hiện Run As → JUnit trông giống như một thẻ báo cáo xấu của trẻ em.

Chú ý: Có testNextState trước testChange trong Listing 4 không đảm bảo rằng JUnit sẽ thực thi testNextState trước khi thực hiện testChange . Nếu bạn có ba phương thức @Test , JUnit có thể thực thi chúng từ đỉnh cao nhất đến đáy, từ đáy đến đỉnh, từ phương pháp giữa đến đỉnh nhất đến đáy, hoặc theo bất kỳ thứ tự nào. JUnit thậm chí có thể tạm dừng ở giữa một thử nghiệm để thực hiện các phần của thử nghiệm khác. Đó là lý do tại sao bạn không bao giờ nên đưa ra các giả định về kết quả của một bài kiểm tra khi bạn viết một bài kiểm tra khác.

Đây là tin tức: Không phải tất cả các phương thức assertEquals đều được tạo như nhau! Hãy tưởng tượng thêm một lớp Driver vào mã dự án của bạn. Lớp Driver Driver không có nghĩa là trình điều khiển máy in hoặc trình điều khiển cọc. Nó có nghĩa là một người lái xe ô tô – một chiếc xe đang đến gần đèn giao thông của bạn. Để biết chi tiết, xem Listing 5.

public class Driver {
public double velocity(TrafficLight light) {
switch (light.state) {
case RED:
return 0.0;
case YELLOW:
return 10.0;
case GREEN:
return 30.0;
default:
return 0.0;
}
}
}

Khi đèn đỏ, vận tốc của người lái là 0,0. Khi đèn có màu vàng, xe đang giảm tốc độ an toàn 10.0. Khi đèn xanh, chiếc xe bay với vận tốc 30,0.

(Trong
ví dụ này, các đơn vị của vận tốc không quan trọng. Họ có thể dặm một
giờ, km mỗi giờ, hoặc bất cứ điều gì. Cách duy nhất nó quan trọng là nếu
xe ở Boston hoặc thành phố New York. Trong trường hợp đó, vận tốc cho VÀNG nên cao hơn nhiều so với vận tốc cho XANH và vận tốc cho ĐỎ không nên là 0,0.)

Để tạo các bài kiểm tra JUnit cho lớp Driver , hãy làm theo các bước từ 1 đến 9 được liệt kê trước đây trong bài viết này, nhưng hãy chắc chắn thực hiện các thay đổi sau:

  • Trong Bước 2, bấm chuột phải hoặc bấm chuột điều khiển vào nhánh Driver.java thay vì nhánh TrafficLight.java .
  • Trong Bước 5, đặt dấu kiểm vào nhánh Driver.
  • Trong Bước 8, loại bỏ các lệnh gọi phương thức thất bại để tạo lớp DriverTest được hiển thị trong Listing 6. (Mã mà bạn nhập được in đậm.)
import static org.junit.Assert.*;
import org.junit.Test;
public class DriverTest {
@Test
public void testVelocity() {
TrafficLight light = new TrafficLight();
light.change(7);
Driver driver = new Driver();
assertEquals(30.0, driver.velocity(light), 0.1);

}
}

Nếu mọi việc suôn sẻ, bài kiểm tra JUnit vượt qua với màu sắc bay. (Nói chính xác hơn, JUnit vượt qua với màu xanh lục!) Vì vậy, việc chạy DriverTest không phải là mới hay thú vị. Điều thú vị là lời gọi tới assertEquals trong Listing 6.

Khi bạn so sánh hai giá trị kép trong một chương trình Java, bạn không có quyền mong đợi sự bình đẳng trên mũi. Nghĩa là, một trong các giá trị kép có thể là 30.000000000 trong khi giá trị kép khác gần với 30.000000001. Một máy tính chỉ có 64 bit để lưu trữ mỗi giá trị kép và sự thiếu chính xác xuất hiện ở đây và đó. Vì vậy, trong JUnit, phương thức assertEquals để so sánh các giá trị kép có tham số thứ ba. Tham số thứ ba đại diện cho phòng ngọ nguậy.

Trong Listing 6, câu lệnh

assertEquals(30.0, driver.velocity(light), 0.1);

nói như sau: Quảng cáo khẳng định rằng giá trị thực của driver.velocity (ánh sáng) nằm trong 0,1 giá trị mong đợi 30.0. Nếu vậy, khẳng định vượt qua. Nếu không, khẳng định thất bại.

Khi bạn gọi assertEquals cho các giá trị kép , việc chọn một lề lỗi tốt có thể khó khăn. Những số liệu này minh họa các loại điều có thể đi sai.

Ở đây, lề lỗi của bạn quá nhỏ.

Ở đó, biên độ lỗi của bạn quá lớn.

May mắn thay, trong ví dụ DriverTest này , tỷ lệ 0,1 là đặt cược rất an toàn. Đây là lý do tại sao:

  • Khi thử nghiệm assertEquals thất bại, nó thất bại nhiều hơn 0,1.
    Thất bại có nghĩa là có giá trị driver.velocity (ánh sáng), chẳng hạn như 0,0 hoặc 10,0.

  • Trong ví dụ này, khi thử nghiệm assertEquals vượt qua, nó có thể đại diện cho sự bình đẳng hoàn toàn, trên mũi.

Giá trị của driver.velocity (ánh sáng) đến trực tiếp từ mã trả về 30.0 trong Liệt kê 5. Không liên quan đến số học. Vì vậy, giá trị của driver.velocity (ánh sáng) và giá trị 30.0 dự kiến sẽ hoàn toàn giống nhau (hoặc gần như giống hệt nhau).

Xem thêm: Java và XML có điểm gì đặc biệt?

Xem thêm: Học lập trình Java web trong mùa dịch nCoV 19