2009-05-07 34 views
21

Lớp sau đây đóng vai trò là bộ kiểm tra chung cho hợp đồng bằng/hashCode. Nó là một phần của một khuôn khổ thử nghiệm được trồng tại nhà.Lý thuyết JUnit cho hashCode/equals contract

  • Bạn nghĩ sao?
  • Tôi có thể (mạnh) kiểm tra lớp học này bằng cách nào?
  • Đó là cách sử dụng tốt lý thuyết của Junit?

Lớp:

@Ignore 
@RunWith(Theories.class) 
public abstract class ObjectTest { 

    // For any non-null reference value x, x.equals(x) should return true 
    @Theory 
    public void equalsIsReflexive(Object x) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assertThat(x.equals(x), is(true)); 
    } 

    // For any non-null reference values x and y, x.equals(y) 
    // should return true if and only if y.equals(x) returns true. 
    @Theory 
    public void equalsIsSymmetric(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(y, is(not(equalTo(null)))); 
     assumeThat(y.equals(x), is(true)); 
     assertThat(x.equals(y), is(true)); 
    } 

    // For any non-null reference values x, y, and z, if x.equals(y) 
    // returns true and y.equals(z) returns true, then x.equals(z) 
    // should return true. 
    @Theory 
    public void equalsIsTransitive(Object x, Object y, Object z) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(y, is(not(equalTo(null)))); 
     assumeThat(z, is(not(equalTo(null)))); 
     assumeThat(x.equals(y) && y.equals(z), is(true)); 
     assertThat(z.equals(x), is(true)); 
    } 

    // For any non-null reference values x and y, multiple invocations 
    // of x.equals(y) consistently return true or consistently return 
    // false, provided no information used in equals comparisons on 
    // the objects is modified. 
    @Theory 
    public void equalsIsConsistent(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     boolean alwaysTheSame = x.equals(y); 

     for (int i = 0; i < 30; i++) { 
      assertThat(x.equals(y), is(alwaysTheSame)); 
     } 
    } 

    // For any non-null reference value x, x.equals(null) should 
    // return false. 
    @Theory 
    public void equalsReturnFalseOnNull(Object x) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assertThat(x.equals(null), is(false)); 
    } 

    // Whenever it is invoked on the same object more than once 
    // the hashCode() method must consistently return the same 
    // integer. 
    @Theory 
    public void hashCodeIsSelfConsistent(Object x) { 
     assumeThat(x, is(not(equalTo(null)))); 
     int alwaysTheSame = x.hashCode(); 

     for (int i = 0; i < 30; i++) { 
      assertThat(x.hashCode(), is(alwaysTheSame)); 
     } 
    } 

    // If two objects are equal according to the equals(Object) method, 
    // then calling the hashCode method on each of the two objects 
    // must produce the same integer result. 
    @Theory 
    public void hashCodeIsConsistentWithEquals(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(x.equals(y), is(true)); 
     assertThat(x.hashCode(), is(equalTo(y.hashCode()))); 
    } 

    // Test that x.equals(y) where x and y are the same datapoint 
    // instance works. User must provide datapoints that are not equal. 
    @Theory 
    public void equalsWorks(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(x == y, is(true)); 
     assertThat(x.equals(y), is(true)); 
    } 

    // Test that x.equals(y) where x and y are the same datapoint instance 
    // works. User must provide datapoints that are not equal. 
    @Theory 
    public void notEqualsWorks(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(x != y, is(true)); 
     assertThat(x.equals(y), is(false)); 
    } 
} 

sử dụng:

import org.junit.experimental.theories.DataPoint; 

public class ObjectTestTest extends ObjectTest { 

    @DataPoint 
    public static String a = "a"; 
    @DataPoint 
    public static String b = "b"; 
    @DataPoint 
    public static String nullString = null; 
    @DataPoint 
    public static String emptyString = ""; 
} 
+0

Nếu tôi đọc chính xác, không phải câu lệnh cuối cùng trong phương thức equalsIsSymmetric của bạn là assertThat, chứ không phải giả định? –

+0

Có, cảm ơn rất nhiều :) – dfa

+0

Vì vậy, bạn đang đi cho một giải pháp nhà trồng, nhưng bạn có biết một số thư viện nguồn mở để làm các loại thử nghiệm phổ biến?(Tôi cũng đề nghị so sánh và serializable.) Tôi sẽ quan tâm đến việc sử dụng một khuôn khổ như vậy. – ivo

Trả lời

8

Một điều cần xem xét: kiểm tra sự phù hợp của đối tượng với hợp đồng bằng nên liên quan đến các trường hợp khác. Đặc biệt, các vấn đề có khả năng xuất hiện với các cá thể của một lớp con hoặc siêu lớp. Joshua Bloch đưa ra một lời giải thích tuyệt vời về những cạm bẫy liên quan trong Effective Java (Tôi đang sử dụng lại liên kết của duffymo, vì vậy anh ta sẽ nhận được tín dụng cho nó) - xem phần dưới Transitivity liên quan đến các lớp Point và ColorPoint.

Đúng, việc triển khai của bạn không ngăn ai đó viết bài kiểm tra liên quan đến trường hợp của lớp con, nhưng vì ObjectTest là một lớp chung chung cho thấy rằng tất cả các điểm dữ liệu phải đến từ một lớp duy nhất). Nó có thể là tốt hơn để loại bỏ các tham số loại hoàn toàn. Chỉ là thức ăn cho sự suy nghĩ.

+0

thực sự! Cảm ơn, tôi loại bỏ tham số kiểu T. – dfa

5

Joshua Bloch đưa ra hợp đồng cho mã băm và tương đương trong chapter 3 of "Effective Java". Có vẻ như bạn đã bao quát rất nhiều. Kiểm tra tài liệu để xem tôi có bỏ sót gì không.

+1

cũng là javadoc cho Object rất chi tiết – dfa

0

Có thể tôi đang thiếu thứ gì đó, nhưng thử nghiệm equalsIsSymmetric trên thực tế chỉ được kiểm tra chính xác nếu bạn có DataPoints có cùng giá trị (ví dụ: String a = "a"; String a2 = "a";) Nếu không thử nghiệm này chỉ được thực hiện khi 2 tham số là một ví dụ (ví dụ equalsIsSymmetric (a, a);). Trong thực tế, bạn kiểm tra lại nếu bằng tuân theo yêu cầu 'phản chiếu' thay vì yêu cầu đối xứng.

+0

vì lý do này thử nghiệm có 'giả định (y.equals (x), là (true))' – dfa

+0

có, nhưng trong thiết lập hiện tại, nó không thể tạo ra ' x 'và' y 'giữ x = y và x.equals (y), vì kiểm tra notEqualsWorks sẽ thất bại trong trường hợp đó. Vì vậy, các thử nghiệm equalsIsSymmetric chỉ được thực hiện cho x và y, nơi x == y. –

+0

vâng. Giả sử thiết lập ở trên, JUnit sẽ thực hiện: equalsIsSymmetric (a, a) và equalsIsSymmetric (b, b). Đúng? – dfa

0

Lý thuyết notEqualsWorks (Object x, Object y) là sai: hai trường hợp riêng biệt có thể vẫn bằng nhau theo phương thức equals của chúng; bạn giả sử các trường hợp khác nhau về mặt logic nếu chúng là các tham chiếu khác nhau.

Sử dụng ví dụ của bạn ở trên, hai datapoints biệt dưới đây (! A = a2) là vẫn như nhau nhưng không kiểm tra notEqualsWorks:

@DataPoint 
public static String a = "a"; 
@DataPoint 
public static String a2 = new String("a"); 
+0

đúng nhưng bạn nên lưu ý rằng lý thuyết có yêu cầu sau đây: "Người dùng phải cung cấp datapoints không bằng nhau". – dfa

0

Phương pháp equalsWorks(Object x, Object y) đang làm bài kiểm tra rất giống như equalsIsReflexive(Object x). Nó nên được loại bỏ.

Tôi cũng nghĩ rằng notEqualsWorks(Object x, Object y) nên được loại bỏ vì nó ngăn cản một để làm các lý thuyết khác với điểm dữ liệu bằng nhau thậm chí nghĩ rằng toàn bộ thử nghiệm là về việc có các đối tượng như vậy.

Nếu không có các điểm dữ liệu như vậy thì phản xạ là điều duy nhất được kiểm tra.

Các vấn đề liên quan