Чому JUnit не надає методів assertNotEquals?


429

Хтось знає, чому JUnit 4 забезпечує, assertEquals(foo,bar)але не assertNotEqual(foo,bar)методи?

Він надає assertNotSame(відповідні assertSame) та assertFalse(відповідні assertTrue), тому здається дивним, що вони не турбували в тому числі assertNotEqual.

До речі, я знаю, що JUnit-аддони надають методи, які я шукаю. Я просто запитую з цікавості.


Принаймні з JUnit 4.12 надається assrtNotEquals. junit.org/junit4/javadoc/4.12/org/junit/…
WebF0x

Відповіді:


403

Я б запропонував вам скористатися новими assertThat()твердженнями стилю, які легко описують усілякі заперечення та автоматично будують опис того, що ви очікували та що ви отримали, якщо твердження не вдасться:

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

Усі три варіанти еквівалентні, виберіть той, який вам найбільше читається.

Щоб використовувати прості назви методів (і дозволити роботі цього напруженого синтаксису), вам потрібен такий імпорт:

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

134
Я ціную вказівник на синтаксис альтернативного твердження, але вказівка ​​в іншому місці не відповідає чому JUnit ніколи не надав assertNotEquals().
seh

14
@seh: То, як я читав це питання, було не про історичний інтерес, а про спосіб сформулювати твердження "ці два об'єкти не рівні" в тесті JUnit. Я відповів на це. Враховуючи "чому є / не було assertNotEqual", я б сказав, що це спеціалізована заява, яка потрібна не так часто, assertEqualsі тому вона буде виражена через загальне assertFalse.
Йоахім Зауер

21
"оберіть той, який вам найбільше читається". Люди, які читають і пишуть одиничні тести, - це програмісти. Чи дійсно вони вважають це більш читабельним, ніж assertNotEqual (objectUnderTest, someOtherObject) або assertFalse (objectUnderTest.equals (someOtherObject))? Мене не переконують API вигадливих програм для
підбору математики

@bacar: для деяких тверджень це в основному питання стилю. Але assertThatце набагато виразніше, ніж обмежений набір assert*доступних методів. Тому ви можете висловити точні обмеження в одному рядку, змусити його (майже) прочитати як англійське речення та отримати змістовне повідомлення, коли аргумент не вдається. Зрозуміло, це не завжди вбивча функція, але коли ви побачили її в дії кілька разів, ви побачите, яку величину вона додає.
Йоахім Зауер

5
@Joachim Я погоджуюся, що assertThatце більш виразно, ніж assert*, але я не думаю, що це виразніше, ніж вираз java, який ви можете вводити всередину та поза assert*виразу взагалі (адже я можу виразити що-небудь у коді Java). Це загальна проблема, з якою я почав стикатися з вільними API-стилями - кожен - це в основному новий DSL, який ви повинні вивчити (коли всі ми вже знаємо Java!). Я гадаю, що Hamcrest досить всюдисущий зараз, коли розумно очікувати, що люди це знатимуть. У мене буде вистава ...
Бакар

154

Є assertNotEqualsв 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
Майте на увазі, один з артефактів jmock (2.6.0) Maven просочується старою версією junit-dep, яка, в свою чергу, має клас Assert без assertNotEquals. Краще виключити це при використанні плюща.
gkephorus

7
Я використовую 4.12, але все ще не можу знайти assertNotEqual. : s
Мубашар

49

Цікаво ж. API Assert не дуже симетричний; для перевірки того, чи об’єкти однакові, передбачено assertSameі assertNotSame.

Звичайно, писати:

assertFalse(foo.equals(bar));

При такому твердженні єдиною інформативною частиною результату є, на жаль, назва методу тестування, тому описове повідомлення слід формувати окремо:

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

Це, звичайно, настільки стомлююче, що краще прокатати своє assertNotEqual. На щастя, можливо, це буде частиною випуску JUnit: JUnit 22


19
Але це менш корисно, оскільки JUnit не може генерувати корисне повідомлення про помилку, вказуючи, наприклад, нерівні значення foo та bar. Справжня причина відмови ховається і перетворюється на просту булеву.
Бен Джеймс

3
Я цілком погоджуюся. Особливо assertFalse потребує належного аргументу повідомлення, щоб отримати висновок, щоб сказати, що дійсно пішло не так.
Мікко Мауну

Я думаю, що це корисно для тестів, що представляють текст. Thnx
Marouane Gazanayi

Проблема з текстом полягає в тому, що він буде застарілим у міру розвитку коду.
Марк Левісон

13

Я стверджую, що відсутність assertNotEqual - це справді асиметрія і робить JUnit трохи менш вивченим. Майте на увазі, що це акуратний випадок, коли додавання методу зменшить складність API, принаймні для мене: Симетрія допомагає керувати більшим простором. Я здогадуюсь, що причина упущення може полягати в тому, що занадто мало людей закликає метод. І все-таки я пам’ятаю час, коли навіть assertFalse не існувало; отже, я маю позитивне сподівання, що метод в кінцевому підсумку може бути доданий, враховуючи, що він не є складним; хоча я визнаю, що існує чимало обхідних шляхів, навіть елегантних.


7

Я приходжу на цю вечірку досить пізно, але я виявив, що форма:

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

можна змусити працювати у більшості випадків, які не є рівними.

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

4
Незважаючи на те, що це працює, проблема полягає в тому, що якщо твердження не вдасться, воно просто скаже «Очікується правда, але було помилковим», або якесь інше незрозуміле твердження. Що було б чудово, якби це очікувалося не 123, а було 123.
Стелс-раббі

6

Я працюю над JUnit в середовищі java 8, використовуючи jUnit4.12

для мене: компілятор не зміг знайти метод assertNotEquals, навіть коли я використовував
import org.junit.Assert;

Тому я змінився
assertNotEquals("addb", string);
на
Assert.assertNotEquals("addb", string);

Тож якщо ви зіткнулися з проблемою щодо assertNotEqualневпізнаваного, то змінити її на Assert.assertNotEquals(,);цю проблему має вирішити вашу проблему


1
Це тому, що методи є статичними, і ви повинні імпортувати їх статично. Використовуйте це, import static org.junit.Assert.*;і ви зможете використовувати всі твердження без назви класу.
Том Стоун

3

Очевидною причиною того, що люди хотіли:

Докладний приклад:

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

vs.

assertNotEqual(1, winningBidderId);

На жаль, оскільки Eclipse за замовчуванням не включає JUnit 4.11, ви повинні бути багатослівними.

Caveat Я не думаю, що '1' потрібно загорнути в Integer.valueOf (), але оскільки я нещодавно повернувся з .NET, не розраховуйте на мою коректність.


1

Краще використовувати Hamcrest для негативних тверджень, а не assertFalse, оскільки в попередньому звіті про випробування буде виявлено різницю для відмови у твердженні.

Якщо ви використовуєте assertFalse, ви просто отримаєте твердження про звіт у звіті. тобто втрачена інформація про причину відмови.


1

Зазвичай я роблю це, коли очікую, що два об'єкти будуть рівними:

assertTrue(obj1.equals(obj2));

і:

assertFalse(obj1.equals(obj2));

коли очікується, що вони будуть нерівними. Я знаю, що це не відповідь на ваше запитання, але це найближче, що я можу отримати. Це може допомогти іншим шукати, що вони можуть зробити у версіях JUnit до JUnit 4.11.


0

Я повністю згоден з точкою зору ОП. Assert.assertFalse(expected.equals(actual))не є природним способом виразити нерівність.
Але я заперечую, що крім цього Assert.assertEquals(), це Assert.assertNotEquals()працює, але не є зручним для користувача, щоб документувати те, що насправді стверджує тест, і розуміти / налагоджувати, як твердження не вдається.
Так що так, JUnit 4.11 і JUnit 5 надає Assert.assertNotEquals()( Assertions.assertNotEquals()у JUnit 5), але я дуже уникаю їх використання.

Як альтернатива, щоб стверджувати стан об'єкта, я, як правило, використовую API відповідності, який легко перекопується в стан об'єкта, який чітко документує наміри тверджень, і це дуже зручно для користувачів, щоб зрозуміти причину відмови твердження.

Ось приклад.
Припустимо, у мене є клас Animal, який я хочу перевірити createWithNewNameAndAge()методом, методом, який створює новий об'єкт Animal, змінюючи його назву та вік, але зберігаючи улюблену їжу.
Припустимо, я використовую, Assert.assertNotEquals()щоб стверджувати, що оригінали та нові об'єкти різні.
Ось клас Animal із хибною реалізацією 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+ (або JUnit 5), як тестовий запуск, так і інструмент затвердження

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

Тест не вдався, як очікувалося, але причина, яка надається розробнику, справді не є корисною. Це просто говорить, що значення повинні бути різними і виводити toString()результат, викликаний фактичним Animalпараметром:

java.lang.AssertionError: Значення повинні бути різними. Актуально: Тварина

[ім'я = scoubi, вік = 10, favoriteFood = сіно]

на org.junit.Assert.fail (Assert.java:88)

Гаразд об'єкти не рівні. Але де проблема?
Яке поле неправильно оцінено в тестованому методі? Один? Два? Усі ?
Щоб виявити це, вам доведеться викопати createWithNewNameAndAge() налагоджувальний пристрій / використовувати налагоджувач, тоді як тестувальний API був би набагато привітнішим, якби він створив для нас різницю між очікуваним та отриманим.


JUnit 4.11 як тестовий бігун і API тесту Matcher як інструмент затвердження

Тут той же самий сценарій тестування, але для затвердження Animalстану використовується AssertJ (відмінний API відповідності тесту) :

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");
}

Звичайно, тест все ще не вдається, але цього разу причина чітко вказана:

java.lang.AssertionError:

Очікуємо:

<["scoubi", 10, "сіно"]>

містити точно (і в тому ж порядку):

<["маленький скаубі", 1, "сіно"]>

але деякі елементи не знайдені:

<["маленький скаубі", 1]>

та інших не очікували:

<["scoubi", 10]>

at junit5.MyTest.assertListNotEquals_AssertJ (MyTest.java:26)

Ми можемо прочитати, що для Animal::getName, Animal::getAge, Animal::getFavoriteFoodзначень повернутої тварини ми очікуємо, що вони мають таке значення:

"little scoubi", 1, "hay" 

але ми мали ці значення:

"scoubi", 10, "hay"

Тож ми знаємо, де розслідуємо: nameі ageне оцінюються правильно. Крім того, факт визначення hayзначення у твердженні Animal::getFavoriteFood()дозволяє також більш тонко стверджувати повернене Animal. Ми хочемо, щоб об’єкти не були однаковими для деяких властивостей, але не обов'язково для всіх властивостей.
Отож, безумовно, використання API matcher набагато чіткіше та гнучкіше.


-1

Послідовність API модулів, чому JUnit не надав, assertNotEquals()- це та сама причина, чому JUnit ніколи не надав такі методи

  • assertStringMatchesTheRegex(regex, str) vs. assertStringDoesntMatchTheRegex(regex, str)
  • assertStringBeginsWith(prefix, str) vs. assertStringDoesntBeginWith(prefix, str)

тобто немає кінця в наданні конкретних методів твердження для тих речей, які вам можуть захотіти у вашій логіці твердження!

Набагато краще , щоб забезпечити компонований тестові примітиви , як equalTo(...), is(...), not(...), regex(...)і нехай програміст Шт їх разом , а не для більш читабельності і здорового глузду.


3
ну чомусь існує аргументEquals (). Це не довелося, але це робить. Питання полягало у відсутності симетрії - чому існує твердження рівності, але не його аналог?
foo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.