Tại sao JUnit không cung cấp các phương thức assertNotEquals?


429

Có ai biết tại sao JUnit 4 cung cấp assertEquals(foo,bar)nhưng không phải là assertNotEqual(foo,bar)phương thức không?

Nó cung cấp assertNotSame(tương ứng với assertSame) và assertFalse(tương ứng với assertTrue), vì vậy có vẻ lạ khi họ không bận tâm bao gồm assertNotEqual.

Nhân tiện, tôi biết rằng JUnit-addons cung cấp các phương thức tôi đang tìm kiếm. Tôi chỉ tò mò thôi.


Ít nhất là kể từ JUnit 4.12, assertNotEquals được cung cấp. junit.org/junit4/javadoc/4.12/org/junit/ từ
WebF0x

Câu trả lời:


403

Tôi khuyên bạn nên sử dụng các assertThat()xác nhận kiểu mới hơn , có thể dễ dàng mô tả tất cả các loại phủ định và tự động xây dựng mô tả về những gì bạn mong đợi và những gì bạn nhận được nếu xác nhận thất bại:

assertThat(objectUnderTest, is(not(someOtherObject)));
assertThat(objectUnderTest, not(someOtherObject));
assertThat(objectUnderTest, not(equalTo(someOtherObject)));

Tất cả ba tùy chọn là tương đương, chọn một trong những bạn thấy dễ đọc nhất.

Để sử dụng tên đơn giản của các phương thức (và cho phép cú pháp căng thẳng này hoạt động), bạn cần những lần nhập này:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

134
Tôi đánh giá cao con trỏ đến cú pháp xác nhận thay thế, nhưng chỉ ra nơi khác không trả lời tại sao JUnit không bao giờ cung cấp assertNotEquals().
seh

14
@seh: Cách tôi đọc câu hỏi không phải là về mối quan tâm lịch sử, mà là về cách hình thành khẳng định "hai đối tượng này không bằng nhau" trong một bài kiểm tra JUnit. Tôi đã trả lời rằng. Xem xét "tại sao là / không có assertNotEqual" Tôi nói rằng đó là bởi vì đó là một khẳng định chuyên biệt không cần thiết thường xuyên assertEqualsvà do đó sẽ được thể hiện thông qua chung chung assertFalse.
Joachim Sauer

21
"Chọn một trong những bạn thấy dễ đọc nhất". Những người đọc và viết bài kiểm tra đơn vị là lập trình viên. Họ có thực sự tìm thấy điều này dễ đọc hơn assertNotEqual (objectUnderTest, someOtherObject) hoặc assertFalse (objectUnderTest.equals (someOtherObject)) không? Tôi không bị thuyết phục bởi các API đối sánh ưa thích - dường như rất khó để lập trình viên khám phá / khám phá cách sử dụng chúng ...
bacar

@bacar: đối với một số khẳng định về cơ bản đó là vấn đề về phong cách. Nhưng assertThatlà rất nhiều biểu cảm hơn so với các assert*phương pháp hạn chế có sẵn. Do đó, bạn có thể diễn đạt các ràng buộc chính xác trong một dòng duy nhất, để nó (gần như) đọc như một câu tiếng Anh nhận được một thông điệp có ý nghĩa khi khẳng định thất bại. Cấp, đó không phải lúc nào cũng là một tính năng giết người, nhưng khi bạn đã thấy nó hoạt động một vài lần, bạn sẽ thấy nó có giá trị bao nhiêu.
Joachim Sauer

5
@Joachim Tôi đồng ý rằng assertThatnó biểu cảm hơn assert*, nhưng tôi không nghĩ nó biểu cảm hơn biểu thức java mà bạn có thể đặt bên trong và ngoài assert*biểu thức nói chung (sau tất cả tôi có thể diễn đạt bất cứ điều gì bằng mã java). Đó là một vấn đề chung mà tôi đã bắt đầu gặp phải với các API kiểu thông thạo - mỗi cái về cơ bản là một DSL mới mà bạn phải học (khi tất cả chúng ta đều đã biết về Java!). Tuy nhiên, tôi cho rằng Hamcrest có mặt ở khắp mọi nơi đủ để mọi người biết đến nó. Tôi sẽ có một vở kịch ...
bacar

154

Có một assertNotEqualstrong JUnit 4.11: https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md#improvements-to-assert-and-assume

import static org.junit.Assert.assertNotEquals;

1
Tâm trí, một trong những đồ tạo tác của jmock (2.6.0) đã rò rỉ một phiên bản cũ của Junit-dep, lần lượt có một lớp Assert mà không có assertNotEquals. Tốt hơn loại trừ rằng khi sử dụng cây thường xuân.
gkephorus

7
Tôi đang sử dụng 4.12 nhưng vẫn không thể tìm thấy assertNotEqual. : s
Mubashar

49

Tôi tự hỏi tương tự. API của Assert không đối xứng lắm; để kiểm tra xem các đối tượng có giống nhau không, nó cung cấp assertSameassertNotSame.

Tất nhiên, nó không quá dài để viết:

assertFalse(foo.equals(bar));

Với một xác nhận như vậy, phần thông tin duy nhất của đầu ra không may là tên của phương thức thử nghiệm, vì vậy thông điệp mô tả nên được tạo riêng:

String msg = "Expected <" + foo + "> to be unequal to <" + bar +">";
assertFalse(msg, foo.equals(bar));

Điều đó là tất nhiên rất tẻ nhạt, tốt hơn là cuộn của riêng bạn assertNotEqual. May mắn thay trong tương lai nó có thể là một phần của vấn đề JUnit : JUnit 22


19
Nhưng điều này ít hữu ích hơn, vì JUnit không thể tạo ra một thông báo lỗi hữu ích cho bạn biết, ví dụ, các giá trị không bằng nhau của foo và bar. Lý do thất bại thực sự được ẩn giấu và biến thành một boolean đơn giản.
Ben James

3
Tôi hoàn toàn đồng ý. Đặc biệt là assertFalse cần đối số thông điệp thích hợp để tạo đầu ra để cho biết điều gì thực sự đã sai.
Mikko Maunu

Tôi nghĩ rằng điều này là hữu ích cho các bài kiểm tra hiện tại văn bản. Thnx
Marouane Gazanayi

Vấn đề với văn bản là nó sẽ bị lỗi thời khi mã phát triển.
Đánh dấu Levison

13

Tôi cho rằng sự vắng mặt của assertNotEqual thực sự là một sự bất cân xứng và làm cho JUnit trở nên ít học hơn một chút. Lưu ý rằng đây là một trường hợp gọn gàng khi thêm một phương thức sẽ làm giảm độ phức tạp của API, ít nhất là đối với tôi: Đối xứng giúp cai trị không gian lớn hơn. Tôi đoán là lý do cho sự thiếu sót có thể là có quá ít người kêu gọi phương pháp này. Tuy nhiên, tôi nhớ một thời gian mà ngay cả assertFalse cũng không tồn tại; do đó, tôi có một kỳ vọng tích cực rằng phương pháp cuối cùng có thể được thêm vào, cho rằng nó không phải là một phương pháp khó; mặc dù tôi thừa nhận rằng có rất nhiều cách giải quyết, thậm chí là thanh lịch.


7

Tôi đến bữa tiệc này khá muộn nhưng tôi đã tìm thấy mẫu đó:

static void assertTrue(java.lang.String message, boolean condition) 

có thể được thực hiện để làm việc cho hầu hết các trường hợp 'không bằng'.

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;

4
Trong khi điều này không hoạt động, vấn đề là nếu xác nhận thất bại, nó sẽ chỉ đơn giản nói "Exepcted true, but false" hoặc một số tuyên bố không rõ ràng khác. Điều tuyệt vời là nếu nó được mong đợi Không phải 123, mà là 123.
Rabbi tàng hình

6

Tôi đang làm việc trên JUnit trong môi trường java 8, sử dụng jUnit4.12

đối với tôi: trình biên dịch không thể tìm thấy phương thức assertNotEquals, ngay cả khi tôi sử dụng
import org.junit.Assert;

Vì vậy, tôi đã thay đổi
assertNotEquals("addb", string);
thành
Assert.assertNotEquals("addb", string);

Vì vậy, nếu bạn đang đối mặt với vấn đề liên quan đến việc assertNotEqualkhông được công nhận, thì hãy đổi nó thành Assert.assertNotEquals(,);vấn đề để giải quyết vấn đề của bạn


1
Đó là bởi vì các phương thức là tĩnh và bạn phải nhập chúng tĩnh. Sử dụng cái này import static org.junit.Assert.*;và bạn sẽ có thể sử dụng tất cả các xác nhận mà không cần tên lớp.
Tom Stone

3

Lý do rõ ràng mà mọi người muốn assertNotEquals () là so sánh các nội trang mà không phải chuyển đổi chúng thành các đối tượng được thổi toàn bộ trước tiên:

Ví dụ dài dòng:

....
assertThat(1, not(equalTo(Integer.valueOf(winningBidderId))));
....

so với

assertNotEqual(1, winningBidderId);

Đáng buồn thay vì Eclipse không bao gồm JUnit 4.11 theo mặc định, bạn phải dài dòng.

Hãy cẩn thận Tôi không nghĩ rằng '1' cần được bọc trong một Integer.valueOf () nhưng vì tôi mới trở về từ .NET nên không tin vào sự đúng đắn của tôi.


1

Tốt hơn là sử dụng Hamcrest cho các xác nhận tiêu cực thay vì khẳng định như trong báo cáo thử nghiệm trước đây sẽ cho thấy sự khác biệt đối với thất bại khẳng định.

Nếu bạn sử dụng assertFalse, bạn sẽ nhận được một lỗi xác nhận trong báo cáo. tức là mất thông tin về nguyên nhân của sự thất bại.


1

Thông thường tôi làm điều này khi tôi mong đợi hai đối tượng bằng nhau:

assertTrue(obj1.equals(obj2));

và:

assertFalse(obj1.equals(obj2));

khi chúng được dự kiến ​​là không đồng đều. Tôi biết rằng đây không phải là một câu trả lời cho câu hỏi của bạn nhưng nó là gần nhất tôi có thể nhận được. Nó có thể giúp những người khác tìm kiếm những gì họ có thể làm trong các phiên bản JUnit trước JUnit 4.11.


0

Tôi hoàn toàn đồng ý với quan điểm của OP. Assert.assertFalse(expected.equals(actual))không phải là một cách tự nhiên để thể hiện sự bất bình đẳng.
Nhưng tôi sẽ lập luận rằng xa hơn Assert.assertEquals(), Assert.assertNotEquals()hoạt động nhưng không thân thiện với người dùng để ghi lại những gì thử nghiệm thực sự khẳng định và để hiểu / gỡ lỗi khi xác nhận thất bại.
Vì vậy, có JUnit 4.11 và JUnit 5 cung cấp Assert.assertNotEquals()( Assertions.assertNotEquals()trong JUnit 5) nhưng tôi thực sự tránh sử dụng chúng.

Thay vào đó, để xác nhận trạng thái của một đối tượng tôi thường sử dụng API đối sánh dễ dàng đào sâu vào trạng thái đối tượng, tài liệu đó rõ ràng là ý định của các xác nhận và rất thân thiện với người dùng để hiểu nguyên nhân của sự thất bại khẳng định.

Đây là một ví dụ.
Giả sử tôi có một lớp Thú mà tôi muốn kiểm tra createWithNewNameAndAge()phương thức, một phương thức tạo ra một đối tượng Động vật mới bằng cách thay đổi tên và tuổi của nó nhưng bằng cách giữ thức ăn yêu thích của nó.
Giả sử tôi sử dụng Assert.assertNotEquals()để khẳng định rằng bản gốc và các đối tượng mới là khác nhau.
Đây là lớp Animal với cách thực hiện thiếu sót createWithNewNameAndAge():

public class Animal {

    private String name;
    private int age;
    private String favoriteFood;

    public Animal(String name, int age, String favoriteFood) {
        this.name = name;
        this.age = age;
        this.favoriteFood = favoriteFood;
    }

    // Flawed implementation : use this.name and this.age to create the 
    // new Animal instead of using the name and age parameters
    public Animal createWithNewNameAndAge(String name, int age) {
        return new Animal(this.name, this.age, this.favoriteFood);
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getFavoriteFood() {
        return favoriteFood;
    }

    @Override
    public String toString() {
        return "Animal [name=" + name + ", age=" + age + ", favoriteFood=" + favoriteFood + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((favoriteFood == null) ? 0 : favoriteFood.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Animal)) return false;

        Animal other = (Animal) obj;
        return age == other.age && favoriteFood.equals(other.favoriteFood) &&
                name.equals(other.name);
    }

}

JUnit 4.11+ (hoặc JUnit 5) vừa là công cụ kiểm tra vừa chạy

@Test
void assertListNotEquals_JUnit_way() {
    Animal scoubi = new Animal("scoubi", 10, "hay");
    Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
    Assert.assertNotEquals(scoubi, littleScoubi);
}

Thử nghiệm thất bại như mong đợi nhưng nguyên nhân cung cấp cho nhà phát triển thực sự không hữu ích. Nó chỉ nói rằng các giá trị phải khác nhau và đưa ra toString()kết quả được gọi trên Animaltham số thực tế :

java.lang.AssertsError: Các giá trị phải khác nhau. Thực tế: Động vật

[name = scoubi, age = 10, favourite = hay]

tại org.junit.Assert.fail (Assert.java:88)

Ok các đối tượng không bằng nhau. Nhưng vấn đề ở đâu?
Trường nào không có giá trị chính xác trong phương pháp thử nghiệm? Một ? Hai ? Tất cả bọn họ ?
Để khám phá ra nó, bạn phải đào sâu vào việc createWithNewNameAndAge() triển khai / sử dụng trình gỡ lỗi trong khi API thử nghiệm sẽ thân thiện hơn nhiều nếu nó tạo cho chúng tôi sự khác biệt giữa dự kiến ​​và dự kiến.


JUnit 4.11 với tư cách là người chạy thử nghiệm và API đối sánh thử nghiệm làm công cụ xác nhận

Ở đây cùng một kịch bản thử nghiệm nhưng sử dụng AssertJ (API đối sánh thử nghiệm tuyệt vời) để đưa ra khẳng định về Animaltrạng thái ::

import org.assertj.core.api.Assertions;

@Test
void assertListNotEquals_AssertJ() {
    Animal scoubi = new Animal("scoubi", 10, "hay");
    Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
    Assertions.assertThat(littleScoubi)
              .extracting(Animal::getName, Animal::getAge, Animal::getFavoriteFood)
              .containsExactly("little scoubi", 1, "hay");
}

Tất nhiên bài kiểm tra vẫn thất bại nhưng lần này lý do được nêu rõ:

java.lang.AssertsError:

Mong đợi:

<["scoubi", 10, "hay"]>

để chứa chính xác (và theo cùng thứ tự):

<["scoubi nhỏ", 1, "hay"]>

nhưng một số yếu tố không được tìm thấy:

<["scoubi nhỏ", 1]>

và những người khác không mong đợi:

<["Scoubi", 10]>

tại junit5.MyTest.assertListNotEquals_AssertJ (MyTest.java:26)

Chúng ta có thể đọc rằng đối với Animal::getName, Animal::getAge, Animal::getFavoriteFoodcác giá trị của Động vật được trả về, chúng ta hy vọng sẽ có các giá trị này:

"little scoubi", 1, "hay" 

nhưng chúng tôi đã có những giá trị sau:

"scoubi", 10, "hay"

Vì vậy, chúng tôi biết nơi điều tra: nameagekhông có giá trị chính xác. Ngoài ra, thực tế chỉ định haygiá trị trong xác nhận Animal::getFavoriteFood()cho phép cũng xác nhận chính xác hơn trả lại Animal. Chúng tôi muốn rằng các đối tượng không giống nhau đối với một số thuộc tính nhưng không nhất thiết phải cho mọi thuộc tính.
Vì vậy, chắc chắn, sử dụng API so khớp rõ ràng và linh hoạt hơn nhiều.


-1

Tính nhất quán API Modulo, tại sao JUnit không cung cấp assertNotEquals()là lý do tương tự tại sao JUnit không bao giờ cung cấp các phương thức như

  • assertStringMatchesTheRegex(regex, str) so với assertStringDoesntMatchTheRegex(regex, str)
  • assertStringBeginsWith(prefix, str) so với assertStringDoesntBeginWith(prefix, str)

tức là không có kết thúc để cung cấp một phương pháp xác nhận cụ thể cho các loại điều bạn có thể muốn trong logic xác nhận của mình!

Tốt hơn để cung cấp nguyên thủy thử nghiệm composable như equalTo(...), is(...), not(...), regex(...)và để cho các lập trình viên phần những cùng nhau thay vì để biết thêm khả năng đọc và sự tỉnh táo.


3
tốt, vì một số lý do, assertEquals () tồn tại. Nó không phải, nhưng nó có. Câu hỏi là về sự thiếu đối xứng - tại sao assertEquals tồn tại mà không phải là đối tác của nó?
foo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.