Почему JUnit не предоставляет методы assertNotEquals?

429

Кто-нибудь знает, почему JUnit 4 предоставляет, assertEquals(foo,bar)но не assertNotEqual(foo,bar)методы?

Это обеспечивает assertNotSame(соответствует assertSame) и assertFalse(соответствует assertTrue), поэтому кажется странным, что они не удосужились в том числе assertNotEqual.

Кстати, я знаю, что JUnit-addons предоставляет методы, которые я ищу. Я просто спрашиваю из любопытства.

Крис Б
источник
По крайней мере, начиная с JUnit 4.12, предоставляется assertNotEquals. 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
msgstr "выбрать тот, который вы найдете наиболее читабельным". Люди, читающие и пишущие юнит-тесты, являются программистами. Действительно ли они находят это более читабельным, чем assertNotEqual (objectUnderTest, someOtherObject) или assertFalse (objectUnderTest.equals (someOtherObject))? Я не уверен, что API-интерфейсы Fancy Matcher - программисту гораздо сложнее изучить / узнать, как их использовать ...
bacar
@bacar: для некоторых утверждений это в основном вопрос стиля. Но assertThatэто гораздо более выразительно, чем ограниченный набор assert*доступных методов. Поэтому вы можете выразить точные ограничения в одной строке, прочитать (почти) как английское предложение и получить значимое сообщение, когда утверждение не выполнено. Конечно, это не всегда убийственная функция, но когда вы несколько раз видели ее в действии, вы увидите, сколько она добавляет.
Иоахим Зауэр
5
@Joachim Я согласен, что assertThatэто более выразительно assert*, но я не думаю, что оно более выразительно, чем Java-выражение, которое вы можете поместить внутри и вне assert*выражения в целом (в конце концов, я могу выразить все что угодно в Java-коде). Это общая проблема, с которой я начал сталкиваться с API-интерфейсами в свободном стиле - каждый из них - это в основном новый DSL, который вы должны изучить (когда мы все уже знаем Java!). Я полагаю, что Хамкрест достаточно повсеместно распространен сейчас, поэтому разумно ожидать, что люди узнают об этом. Я буду играть ...
Bacar
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
Имейте в виду, что один из артефактов jvenck (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 ) ;
user903724
источник
4
Хотя это работает, проблема заключается в том, что если утверждение не выполнено, оно просто скажет «Исключено истина, но было ложным», или какое-то другое неясное утверждение. Что было бы здорово, так это если бы ожидалось не 123, а 123.
Stealth Rabbi
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

Очевидная причина, по которой люди хотели assertNotEquals (), заключалась в том, чтобы сравнивать встроенные функции без предварительной конвертации их в полноразмерные объекты:

Подробный пример:

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

против

assertNotEqual(1, winningBidderId);

К сожалению, поскольку Eclipse по умолчанию не включает JUnit 4.11, вы должны быть многословны.

Предостережение Я не думаю, что '1' нужно заключать в Integer.valueOf (), но, поскольку я только что вернулся из .NET, не рассчитывайте на мою правильность.

Марк Левисон
источник
1

Лучше использовать Hamcrest для отрицательных утверждений, а не assertFalse, так как в первом отчете об испытаниях будет показана разница при неудаче утверждений.

Если вы используете assertFalse, вы просто получаете ошибку подтверждения в отчете. т.е. потеряли информацию о причине сбоя.

Крис Келли
источник
1

Обычно я делаю это, когда ожидаю, что два объекта будут равны:

assertTrue(obj1.equals(obj2));

а также:

assertFalse(obj1.equals(obj2));

когда ожидается, что они будут неравными. Я знаю, что это не ответ на ваш вопрос, но это самый близкий, который я могу получить. Это может помочь другим в поиске того, что они могут делать в версиях JUnit до JUnit 4.11.

Willempie
источник
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: Значения должны быть разными. Актуально: Животное

[name = scoubi, возраст = 10, любимая еда = сено]

в org.junit.Assert.fail (Assert.java:88)

Хорошо, объекты не равны. Но в чем проблема?
Какое поле неправильно оценивается в тестируемом методе? Один ? Два ? Все они ?
Чтобы обнаружить его, вам нужно покопаться в createWithNewNameAndAge() реализации / использовать отладчик, в то время как API тестирования был бы гораздо более дружественным, если бы он сделал для нас разницу между ожидаемым и полученным.


JUnit 4.11 в качестве тестового прогона и тестовый Matcher API в качестве инструмента подтверждения

Вот тот же сценарий тестирования, но использующий AssertJ (отличный API для проверки соответствия), чтобы установить Animalсостояние:

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, "hay"]>

содержать точно (и в том же порядке):

<["little scoubi", 1, "hay"]>

но некоторые элементы не были найдены:

<["little scoubi", 1]>

и другие не ожидались

<["scoubi", 10]>

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

Мы можем прочитать, что для Animal::getName, Animal::getAge, Animal::getFavoriteFoodзначений возвращаемого Animal мы ожидаем получить следующие значения:

"little scoubi", 1, "hay" 

но у нас были эти значения:

"scoubi", 10, "hay"

Итак, мы знаем, где исследуем: nameи ageне правильно оценили. Кроме того, факт указания hayзначения в утверждении Animal::getFavoriteFood()позволяет также более точно утверждать возвращаемое Animal. Мы хотим, чтобы объекты не были одинаковыми для некоторых свойств, но не обязательно для всех свойств.
Так что, безусловно, использование API-интерфейса для сопоставления является гораздо более понятным и гибким.

davidxxx
источник
-1

Непротиворечивость по модулю API, почему JUnit не предоставил, assertNotEquals()является той же самой причиной, по которой JUnit никогда не предоставлял такие методы, как

  • assertStringMatchesTheRegex(regex, str) против assertStringDoesntMatchTheRegex(regex, str)
  • assertStringBeginsWith(prefix, str) против assertStringDoesntBeginWith(prefix, str)

т. е. нет конца предоставлению конкретных методов утверждения для тех вещей, которые вам могут понадобиться в логике утверждения!

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

fatuhoku
источник
3
ну, по некоторым причинам, assertEquals () существует. Это не обязательно, но это так. Вопрос был об отсутствии симметрии - почему существует assertEquals, а не его аналог?
Foo