2013-08-13 45 views
5

Dưới đây là yêu cầu của tôi cho kiểm tra đơn vị:Đơn vị kiểm tra - thực hiện bình đẳng chỉ để tạo điều kiện thử nghiệm

  1. Tôi muốn đơn vị kiểm tra lớp học sản xuất của tôi
  2. Tôi muốn tách mã kiểm tra và mã sản xuất ngoài như vậy tôi chỉ có thể phát hành mã sản xuất

Điều này có vẻ như yêu cầu hợp lý. Tuy nhiên, một vấn đề luôn nảy sinh khi tôi cần sử dụng các phương thức như assertEquals trên các đối tượng vì chúng đòi hỏi phương thức equals bị ghi đè. Phương pháp equals sẽ phải được triển khai trong các lớp sản xuất nhưng thực tế chỉ được sử dụng để thử nghiệm. Điều này trở nên tồi tệ hơn khi thực hành mã hóa tốt quy định rằng nếu equals bị ghi đè, thì cũng cần thực hiện hashCode dẫn đến mã sản xuất không sử dụng nhiều hơn làm xáo trộn các lớp sản xuất.

Đây là một ví dụ đơn giản với một mô hình User (IntelliJ autoimplemented equalshashCode)

public class User 
{ 
    public long id; 
    public long companyId; 
    public String name; 
    public String email; 
    public long version; 

    @Override 
    public boolean equals(Object o) 
    { 
     if(this == o) return true; 
     if(o == null || getClass() != o.getClass()) return false; 
     User user = (User) o; 
     if(companyId != user.companyId) return false; 
     if(id != user.id) return false; 
     if(version != user.version) return false; 
     if(!email.equals(user.email)) return false; 
     if(!name.equals(user.name)) return false; 
     return true; 
    } 

    @Override 
    public int hashCode() 
    { 
     int result = (int) (id^(id >>> 32)); 
     result = 31 * result + (int) (companyId^(companyId >>> 32)); 
     result = 31 * result + name.hashCode(); 
     result = 31 * result + email.hashCode(); 
     result = 31 * result + (int) (version^(version >>> 32)); 
     return result; 
    } 
} 

Vì nó có thể được nhìn thấy, equalshashCode chiếm rất nhiều không gian và rối cái class.

Một giải pháp cho vấn đề có thể là tạo một lớp, UserTester, có thể sử dụng phương thức assertUserEquals thay vì ví dụ. JUnit's assertEquals.

Một giải pháp khác có thể là tạo một UserComparator. Tuy nhiên, có vẻ như JUnit không có bất kỳ số assertEquals nào mất Comparator.

Thực tiễn tốt nhất về điểm này là gì?

Trả lời

1

Uniutils có phản ánh hoàn hảo bằng phương pháp bạn có thể sử dụng để kiểm tra đơn vị. Bằng cách này, mã sản xuất của bạn vẫn rõ ràng từ tất cả các công cụ kiểm tra này.

public class User { 

    private long id; 
    private String first; 
    private String last; 

    public User(long id, String first, String last) { 
     this.id = id; 
     this.first = first; 
     this.last = last; 
    } 
} 

Sau đó trong bài kiểm tra:

User user1 = new User(1, "John", "Doe"); 
User user2 = new User(1, "John", "Doe"); 
assertReflectionEquals(user1, user2); 

Nếu bạn đang sử dụng Mockito nó có đó là phương tiện riêng để làm điều tương tự:

Mockito.verify(userDeleter).delete(Mockito.refEq(user)); 
+0

Chỉ cần kiểm tra tài liệu của họ. Có vẻ như bạn nên sử dụng 'assertReflectionEquals' thay vì' assertEquals'. Có vẻ như đó là giải pháp cho vấn đề của tôi. – foens

+0

Tệ của tôi, cảm ơn vì đã chú ý – Jk1

+0

Ehh - Chỉ tìm thấy một vấn đề nhỏ mà tôi chưa trình bày trong câu hỏi của mình. Điều gì về việc sử dụng các khuôn khổ như [mockito] (https://code.google.com/p/mockito/) để chế nhạo? Khi sử dụng các phương thức như 'verify (userDeleter) .delete (user)', nó cũng sử dụng 'equals' để kiểm tra các đối số. Bạn có biết giải pháp nào cho vấn đề đó không? – foens

0

Không phải là hiệu quả nhất nhưng một trong những cách tốt là so sánh các trường bằng cách sử dụng sự phản chiếu.

public class Foo { 

    int x; 

    Foo(int in){ 
    x = in; 
    } 

    public static void main(String[] args) throws Exception{ 

     Foo o1 = new Foo(1),o2= new Foo(1); 
     boolean allMatch = true; 
     Class<?> c = Class.forName("Foo"); 
     Field[] fields = c.getDeclaredFields(); 

     for(Field f: fields){ 
      allMatch &= f.get(o1)==f.get(o2); 
     } 
    } 
} 
+0

Có vẻ như câu trả lời của Jk1 chỉ đơn giản là một phiên bản đóng gói gọn gàng về ý tưởng của bạn. Có thực sự là chậm? – foens

+0

Phản ánh tốt không có chi phí của nó, nhưng điều đó phụ thuộc vào cách thức và thời điểm bạn đang sử dụng nó. Tôi nghĩ rằng sử dụng nó trong giai đoạn thử nghiệm không phải là một vấn đề, nhưng sau đó một lần nữa nó thực sự phụ thuộc vào bao nhiêu cuộc gọi phản chiếu bạn thực hiện. Ngoài ra, nó tốt hơn để sử dụng một thư viện hiện có nhưng biết việc thực hiện có thể giúp bạn tùy chỉnh giải pháp của bạn. – rocketboy

0

tôi thấy hai điều khác nhau:

  1. Đôi khi nó không phải là mong muốn ghi đè hashcode & bằng. Nó có thể thay đổi hành vi của chương trình của bạn và nó có thể làm tổn hại đến hiệu suất của bạn, xem Java Overriding hashCode() method has any Performance issue?

  2. Nếu không có yêu cầu của khách hàng để ghi đè mã băm & bằng, như đối tượng giá trị của nó, bạn sẽ không làm điều đó.Bạn nên cung cấp mã như vậy mà chính xác đáp ứng tiêu chí của khách hàng, không nhiều hơn. Thử nghiệm của bạn nên đối phó với việc triển khai ban đầu trong đối tượng mặc định.