Giới thiệu Junit – testingvn.com

Post

by hoangliensonmt » Mon 23 Aug, 2010 3:27 pm

Giới thiệu Junit

UNIT TESTING

1. Junit là gì?
Các ngôn ngữ lập trình như như ASP, C++, C, Delphi, Perl, PHP, REBOL, Python,… đều có bộ hỗ trợ Unit Test riêng của nó. JUnit là một framework được dùng cho Unit Test trong Java. JUnit được xây dựng bởi Erich Gamma và Kent Beck, hai người nổi tiếng nhất về lập trình XP.

Trong JUnit có các Test Case là các lớp của Java, các lớp này bao gồm một hay nhiều các phương thức cần kiểm tra, và Test Case này lại được nhóm với nhau để tạo thành Test Suite. Mỗi phương thức thử trong

Những người lập trình không muốn bị ngắt quãng trong một thời gian dài trong khi các phép thử đang chạy, do đó các phép thử mà càng chạy lâu thì sẽ có nhiều khả năng là các lập trình viên sẽ bỏ qua bước này. Các phép thử được thiết kế để khi chạy mà không cần có sự can thiệp của con người.

Mỗi phép thử trong JUnit là một phương thức public, không có đối số và được bắt đầu bằng chữ test ( testXXX()). Nếu chúng ta không tuân thủ theo qui tắc này thì JUnit sẽ không xác định được các phương thức test một cách tự động.

2. Lợi ích của Junit
JUnit tránh cho người lập trình phải làm đi làm lại những việc kiểm thử nhàm chán bằng cách tách biệt mã kiểm thử ra khỏi mã chương trình, đồng thời tự động hóa việc tổ chức và thi hành các bộ số liệu kiểm thử.

Thoạt tiên, khi sử dụng JUnit, ta có thể có cảm giác là JUnit chỉ làm mất thêm thời gian cho việc kiểm thử: Thay vì phải viết thêm các lớp và phương thức mới phục vụ cho công tác kiểm thử, ta có thể soạn nhanh một bộ số liệu rồi viết ngay vào trong phương thức main() và quan sát ngay kết quả kiểm thử. Vì quá trình soạn số liệu và quá trình kiểm thử diễn ra đồng thời, nên ta sẽ dễ dàng nhận biết được ngay chương trình đã chạy đúng trên bộ số liệu kiểm thử hay không, mà không cần nhìn vào tín hiệu “xanh” mà JUnit có thể hỗ trợ.

Nhưng khi tổ chức lại chương trình cho hợp lý hơn (refactoring) hoặc khi phải thay đổi chương trình để phục vụ cho nhu cầu mới, các bộ số liệu kiểm thử trước đây sẽ cần được sử dụng lại để chắc chắn rằng những thay đổi trong chương trình không làm phương hại đến những thành quả trước đó, lúc này ta sẽ phải mất thời gian để tìm hiểu lại xem bộ số liệu trước đây sẽ tương ứng với kết xuất gì vì ta không thể nhớ hết mọi hoạt động kiểm thử đã diễn ra. Việc nhớ lại những kiểm thử đã qua sẽ chẳng thú vị vì không đem đến cho ta điều gì mới. Nếu phải kiểm thử trên những bộ số liệu lớn thì gánh nặng của việc tổ chức kiểm thử sẽ chồng chất thêm. JUnit giúp người lập trình tự động hóa các công việc nhàm chán, và chỉ cần nhìn thấy tín hiệu “xanh” là người lập trình có thể an tâm rằng module đã được lập trình đúng.

3. Các phương thức trong JUnit

3.1 assertXXX()
Các phương thức dạng assertXXX() được dùng để kiểm tra các điều kiện khác nhau. Dưới đây là mô tả các phương thức assertXXX() khác nhau có trong lớp junit.framework.Assert.

• Boolean assertEquals(): So sánh hai giá trị để kiểm tra bằng nhau. Phép thử thất bại nếu hai giá trị không bằng nhau.
• Boolean assertFalse(): Đánh giá biểu thức logic. Phép thử thất bại nếu biểu thức đúng.
• Boolean assertNotNull(): So sánh tham chiếu của một đối tượng với Null. Phép thử thất bại nếu tham chiếu đối tượng Null.
• Boolean assertNotSame(): So sánh địa chỉ vùng nhớ của hai tham chiếu hai đối tượng bằng cách sử dụng toán tử ==. Phép thử thất bại trả về nếu cả hai đều tham chiếu đến cùng một đối tượng.
• Boolean assertNull(): So sánh tham chiếu của một đối tượng với giá trị Null. Phép thử thất bại nếu đối tượng không là Null.
• Boolean assertSame(): So sánh địa chỉ vùng nhớ của hai tham chiếu đối tượng bằng cách sử dụng toán tử ==. Phép thử thất bại nếu cả hai không tham chiếu đến cùng một đối tượng.
• Boolean assertTrue(): Đánh giá một biểu thức logic. Phép thử thất bại nếu biểu thức sai.
• void fail(): Phương thức này làm cho test hiện tại thất bại, phương thức này thường được sử dụng khi xử lý các ngoại lệ.

Chúng ta có thể chỉ sử dụng phương thức assertTrue()cho gần như là hầu hết các test, tuy nhiên việc sử dụng các phương thức assertXXX() cụ thể sẽ tiện lợi hơn cho các test của bạn trong trường hợp cung cấp các thông báo mô tả thất bại. Tất cả các phương thức trên đều nhận vào một String không bắt buộc làm tham số đầu tiên. Khi được xác định, tham số này cung cấp một message mô tả test thất bại. Điều này giúp cho việc sửa lỗi được dễ dàng hơn.

3.2 setUp() và tearDown()
Hai phương thức này là một phần của lớp junit.framework.TestCase. Khi sử dụng hai phương thức này sẽ giúp chúng ta tránh được việc trùng mã khi nhiều test cùng chia sẻ nhau ở phần khởi tạo và dọn dẹp các biến.
JUnit tuân thủ theo một dãy có thứ tự các sự kiện khi chạy các test. Đầu tiên, nó tạo ra một thể hiện mới của Test Case ứng với mỗi phương thức thử. Từ đó, nếu bạn có 5 phương thức thử thì JUnit sẽ tạo ra 5 thể hiện của Test Case. Vì lý do đó, các biến thể hiện không thể được sử dụng để chia sẻ trạng thái giữa các phương thức test. Sau khi tạo xong tất cả các đối tượng test case, JUnit tuân theo các bước sau cho mỗi phương thức test:

  • • Gọi phương thức setUp() của test case
    • Gọi phương thức thử
    • Gọi phương thức tearDown() của test case

Quá trình này được lặp lại đối với mỗi phương thức thử trong Test Case.
Thông thường chúng ta có thể bỏ qua phương thức tearDown() vì mỗi phương thức thử riêng không phải là những tiến trình chạy tốn nhiều thời gian và các đối tượng được thu dọn khi máy áo Java (JVM) thoát. Phương thức tearDown() có thể được sử dụng khi test của bạn thực hiện những thao tác như mở kết nối đến cơ sở dữ liệu hay sử dụng các loại tài nguyên khác của hệ thống và bạn cần phải dọn dẹp ngay lập tức. Nếu bạn chạy một bộ bao gồm một số lượng lớn các unit test thì khi chúng ta trỏ tham chiếu của các đối tượng đến null bên trong thân phương thức tearDown() sẽ giúp cho bộ dọn rác lấy lại bộ nhớ khi các test khác chạy.

3.3 Tổ chức các phép thử
Mỗi phép thử ( Test Case)chỉ nên kiểm tra phần cụ thể của một chức năng nào đó. Chúng ta không nên kết hợp nhiều phép thử không liên quan đến nhau vào trong cùng một phương thức testXXX(). Một phương thức thử có thể chứa nhiều hơn một phương thức assertXXX(). Khi chúng ta cần kiểm tra một dãy các điều kiện và một phép thử thất bại sẽ khiến các test theo sau thất bại , khi đó chúng ta có thể kết hợp nhiều phương thức assertXXX() vào trong một phương thức thử.

Thông thường thì JUnit sẽ tự động tạo ra các Test Suite ứng với mỗi Test Case. Tuy nhiên, chúng ta cũng có thể tự tạo ra các Test Suite riêng của mình bằng cách tổ chức các Test Case vào Test Suite, được hỗ trợ bởi lớp junit.framework.TestSuite. Test Suite cho phép kết hợp các Test Case để tạo ra một phép thử tổng quát.
Khi bạn sử dụng giao diện text hay graphic, JUnit sẽ tìm phương thức sau trong Test Case của bạn:

public static Test suite() { … }

Nếu không thấy phương thức trên, JUnit sẽ sử dụng kỹ thuật ánh xạ để tự động xác định tất cả các phương thức testXXX() trong Test Case của bạn, rồi thêm chúng vào một Test Suite. Sau đó nó sẽ chạy tất cả các test trong suite này. Bạn có thể tạo ra bản sao hành vi của phương thức suite() mặc định như sau:

Code: Select all

public class TestXXX extends TestCase{
…
public static Test suite() {
return new TestSuite(TestXXX.class);
}
}

Code: Select all

public static Test suite() {
TestSuite suite = new TestSuite(TestXXX.class);
suite.addTest(new TestSuite(TestYYY.class));
return suite;
}

Code: Select all

public static Test suite() {
//Chạy toàn bộ test suite 10 lần
return new RepeatedTest(new TestSuite(TestXXX.class), 10);
}

Để sử dụng Junit Test ta click chuột phải vào Java Project đã tạo chọn Properties –> Java Build Path –> Library–> Add Library –> Junit–> Tùy chọn Junit 3 hoạc 4–> Finish
Ta tạo một class như sau: Money.java

Code: Select all

package Money;
public class Money {
private double amount;
private String currency;
public Money(double amount, String currency) {
this.amount = amount;
this.currency = currency;
}
public boolean equals (Money a){
if (this.amount==a.amount&& this.currency.equals(a.currency))
return true;
else return false; }
public Money AddMoney (Money a, Money b){
if (a.equals(b))
{Money c= new Money(0,a.currency);
c.amount=a.amount+b.amount;
return c; }
else return null;
}

• Test Case Class: Được đặt tên [classname]Test.java, với classname là tên của lớp được test.
• Test Case Method: Được đặt tên test[methodname], với methodname là tên của phương thức được test.
• Test Suite: mặc định tên cho Eclipse là AllTests.java
Ta chỉ cần tiếp tục chọn Next –> chọn phương thức cần test –> Finish.
Sau khi tạo đựoc Test class ta viết phương thức test. Đối với class trên ta có phương thức test như sau:

Code: Select all

package Money;
import static org.junit.Assert.*;
import org.junit.Test;
public class MoneyTest {
@Test
public void testEqualsMoney() {
Money m1 = new Money(200, "VND");
Money m2 = new Money(1000, "VND");
assertTrue(m1.equals(new Money(200, "VND")));
assertFalse(m1.equals(m2));
}
@Test
public void testAddMoney() {
Money m1 = new Money(200, "VND");
Money m2 = new Money(1000, "VND");
Money result = m1.AddMoney(m1,m2);
Money expected = new Money(1200, "VND");
assertTrue("Result",expected.equals(result));
}
}

Junit hỗ trợ hiển thị một tín hiệu xanh để báo hiệu cho người lập trình biết Test Case đã pass, tín hiệu đỏ để báo hiệu đã fail.

Với test class trên thì kết quả sẽ là tín hiệu xanh.

Bạn hãy thử thay đổi Code để xem tín hiệu đỏ nhé.

Các ngôn ngữ lập trình như như ASP, C++, C, Delphi, Perl, PHP, REBOL, Python,… đều có bộ hỗ trợ Unit Test riêng của nó. JUnit là một framework được dùng cho Unit Test trong Java. JUnit được xây dựng bởi Erich Gamma và Kent Beck, hai người nổi tiếng nhất về lập trình XP.Trong JUnit có các Test Case là các lớp của Java, các lớp này bao gồm một hay nhiều các phương thức cần kiểm tra, và Test Case này lại được nhóm với nhau để tạo thành Test Suite. Mỗi phương thức thử trong JUnit phải được thực thi nhanh chóng. Tốc độ ở đây là điều tối quan trọng vì càng nhiều phép thử được viết và tích hợp vào bên trong quá trình phần mềm thì càng tốn nhiều thời gian để hơn cho việc chạy toàn bộ Test Suite Những người lập trình không muốn bị ngắt quãng trong một thời gian dài trong khi các phép thử đang chạy, do đó các phép thử mà càng chạy lâu thì sẽ có nhiều khả năng là các lập trình viên sẽ bỏ qua bước này. Các phép thử được thiết kế để khi chạy mà không cần có sự can thiệp của con người.Mỗi phép thử trong JUnit là một phương thức public, không có đối số và được bắt đầu bằng chữ test ( testXXX()). Nếu chúng ta không tuân thủ theo qui tắc này thì JUnit sẽ không xác định được các phương thức test một cách tự động.JUnit tránh cho người lập trình phải làm đi làm lại những việc kiểm thử nhàm chán bằng cách tách biệt mã kiểm thử ra khỏi mã chương trình, đồng thời tự động hóa việc tổ chức và thi hành các bộ số liệu kiểm thử.Thoạt tiên, khi sử dụng JUnit, ta có thể có cảm giác là JUnit chỉ làm mất thêm thời gian cho việc kiểm thử: Thay vì phải viết thêm các lớp và phương thức mới phục vụ cho công tác kiểm thử, ta có thể soạn nhanh một bộ số liệu rồi viết ngay vào trong phương thức main() và quan sát ngay kết quả kiểm thử. Vì quá trình soạn số liệu và quá trình kiểm thử diễn ra đồng thời, nên ta sẽ dễ dàng nhận biết được ngay chương trình đã chạy đúng trên bộ số liệu kiểm thử hay không, mà không cần nhìn vào tín hiệu “xanh” mà JUnit có thể hỗ trợ.Nhưng khi tổ chức lại chương trình cho hợp lý hơn (refactoring) hoặc khi phải thay đổi chương trình để phục vụ cho nhu cầu mới, các bộ số liệu kiểm thử trước đây sẽ cần được sử dụng lại để chắc chắn rằng những thay đổi trong chương trình không làm phương hại đến những thành quả trước đó, lúc này ta sẽ phải mất thời gian để tìm hiểu lại xem bộ số liệu trước đây sẽ tương ứng với kết xuất gì vì ta không thể nhớ hết mọi hoạt động kiểm thử đã diễn ra. Việc nhớ lại những kiểm thử đã qua sẽ chẳng thú vị vì không đem đến cho ta điều gì mới. Nếu phải kiểm thử trên những bộ số liệu lớn thì gánh nặng của việc tổ chức kiểm thử sẽ chồng chất thêm. JUnit giúp người lập trình tự động hóa các công việc nhàm chán, và chỉ cần nhìn thấy tín hiệu “xanh” là người lập trình có thể an tâm rằng module đã được lập trình đúng.Các phương thức dạng assertXXX() được dùng để kiểm tra các điều kiện khác nhau. Dưới đây là mô tả các phương thức assertXXX() khác nhau có trong lớp junit.framework.Assert.• Boolean assertEquals(): So sánh hai giá trị để kiểm tra bằng nhau. Phép thử thất bại nếu hai giá trị không bằng nhau.• Boolean assertFalse(): Đánh giá biểu thức logic. Phép thử thất bại nếu biểu thức đúng.• Boolean assertNotNull(): So sánh tham chiếu của một đối tượng với Null. Phép thử thất bại nếu tham chiếu đối tượng Null.• Boolean assertNotSame(): So sánh địa chỉ vùng nhớ của hai tham chiếu hai đối tượng bằng cách sử dụng toán tử ==. Phép thử thất bại trả về nếu cả hai đều tham chiếu đến cùng một đối tượng.• Boolean assertNull(): So sánh tham chiếu của một đối tượng với giá trị Null. Phép thử thất bại nếu đối tượng không là Null.• Boolean assertSame(): So sánh địa chỉ vùng nhớ của hai tham chiếu đối tượng bằng cách sử dụng toán tử ==. Phép thử thất bại nếu cả hai không tham chiếu đến cùng một đối tượng.• Boolean assertTrue(): Đánh giá một biểu thức logic. Phép thử thất bại nếu biểu thức sai.• void fail(): Phương thức này làm cho test hiện tại thất bại, phương thức này thường được sử dụng khi xử lý các ngoại lệ.Chúng ta có thể chỉ sử dụng phương thức assertTrue()cho gần như là hầu hết các test, tuy nhiên việc sử dụng các phương thức assertXXX() cụ thể sẽ tiện lợi hơn cho các test của bạn trong trường hợp cung cấp các thông báo mô tả thất bại. Tất cả các phương thức trên đều nhận vào một String không bắt buộc làm tham số đầu tiên. Khi được xác định, tham số này cung cấp một message mô tả test thất bại. Điều này giúp cho việc sửa lỗi được dễ dàng hơn.Hai phương thức này là một phần của lớp junit.framework.TestCase. Khi sử dụng hai phương thức này sẽ giúp chúng ta tránh được việc trùng mã khi nhiều test cùng chia sẻ nhau ở phần khởi tạo và dọn dẹp các biến.JUnit tuân thủ theo một dãy có thứ tự các sự kiện khi chạy các test. Đầu tiên, nó tạo ra một thể hiện mới của Test Case ứng với mỗi phương thức thử. Từ đó, nếu bạn có 5 phương thức thử thì JUnit sẽ tạo ra 5 thể hiện của Test Case. Vì lý do đó, các biến thể hiện không thể được sử dụng để chia sẻ trạng thái giữa các phương thức test. Sau khi tạo xong tất cả các đối tượng test case, JUnit tuân theo các bước sau cho mỗi phương thức test:Quá trình này được lặp lại đối với mỗi phương thức thử trong Test Case.Thông thường chúng ta có thể bỏ qua phương thức tearDown() vì mỗi phương thức thử riêng không phải là những tiến trình chạy tốn nhiều thời gian và các đối tượng được thu dọn khi máy áo Java (JVM) thoát. Phương thức tearDown() có thể được sử dụng khi test của bạn thực hiện những thao tác như mở kết nối đến cơ sở dữ liệu hay sử dụng các loại tài nguyên khác của hệ thống và bạn cần phải dọn dẹp ngay lập tức. Nếu bạn chạy một bộ bao gồm một số lượng lớn các unit test thì khi chúng ta trỏ tham chiếu của các đối tượng đến null bên trong thân phương thức tearDown() sẽ giúp cho bộ dọn rác lấy lại bộ nhớ khi các test khác chạy.Mỗi phép thử ( Test Case)chỉ nên kiểm tra phần cụ thể của một chức năng nào đó. Chúng ta không nên kết hợp nhiều phép thử không liên quan đến nhau vào trong cùng một phương thức testXXX(). Một phương thức thử có thể chứa nhiều hơn một phương thức assertXXX(). Khi chúng ta cần kiểm tra một dãy các điều kiện và một phép thử thất bại sẽ khiến các test theo sau thất bại , khi đó chúng ta có thể kết hợp nhiều phương thức assertXXX() vào trong một phương thức thử.Thông thường thì JUnit sẽ tự động tạo ra các Test Suite ứng với mỗi Test Case. Tuy nhiên, chúng ta cũng có thể tự tạo ra các Test Suite riêng của mình bằng cách tổ chức các Test Case vào Test Suite, được hỗ trợ bởi lớp junit.framework.TestSuite. Test Suite cho phép kết hợp các Test Case để tạo ra một phép thử tổng quát.Khi bạn sử dụng giao diện text hay graphic, JUnit sẽ tìm phương thức sau trong Test Case của bạn:public static Test suite() { … }Nếu không thấy phương thức trên, JUnit sẽ sử dụng kỹ thuật ánh xạ để tự động xác định tất cả các phương thức testXXX() trong Test Case của bạn, rồi thêm chúng vào một Test Suite. Sau đó nó sẽ chạy tất cả các test trong suite này. Bạn có thể tạo ra bản sao hành vi của phương thức suite() mặc định như sau:Bằng cách truyền đối tượng TestXXX.class vào hàm khởi tạo TestSuite, chúng ta đang thông báo cho JUnit biết để xác định tất cả các phương thức TestXXX () của lớp TestXXX và thêm chúng vào suite. Chúng ta có thể kết hợp nhiều suite vào các suite khác. Ví dụ như sau:Trong một số trường hợp chúng ta muốn chạy test nào đó lặp đi lặp lại nhiều lần để đo hiệu suất hay phân tích các vấn đề trục trặc cần phải giải quyết, JUnit cung cấp cho chúng ta lớp junit.extension. RepeatedTest dùng để giải quyết vấn đề này. Lớp này sẽ giúp chúng ta chạy các test lặp đi lặp lại.Như đã biết, chúng ta dùng cặp từ khóa try/catch để bắt các exception như mong đợi. Khi exception chúng ta mong đợi không xảy ra, ta sẽ gọi phương thức fail() của JUnit.Để sử dụng Junit Test ta click chuột phải vào Java Project đã tạo chọn Properties –> Java Build Path –> Library–> Add Library –> Junit–> Tùy chọn Junit 3 hoạc 4–> FinishTa tạo một class như sau: Money.javaSau đó click chuột phải vào class chọn New –> Junit Test Case –> chương trình sẽ tự đặt tên theo quy ước chuẩn như sau:• Test Case Class: Được đặt tên [classname]Test.java, với classname là tên của lớp được test.• Test Case Method: Được đặt tên test[methodname], với methodname là tên của phương thức được test.• Test Suite: mặc định tên cho Eclipse là AllTests.javaTa chỉ cần tiếp tục chọn Next –> chọn phương thức cần test –> Finish.Sau khi tạo đựoc Test class ta viết phương thức test. Đối với class trên ta có phương thức test như sau:Junit Test.và au đó click chuột phải vào test class chọn Run AsJunit hỗ trợ hiển thị một tín hiệu xanh để báo hiệu cho người lập trình biết Test Case đã pass, tín hiệu đỏ để báo hiệu đã fail.Với test class trên thì kết quả sẽ là tín hiệu xanh.Bạn hãy thử thay đổi Code để xem tín hiệu đỏ nhé.