Почему JUnit не предоставляет методы AssertNotEquals?
Вопрос
Кто-нибудь знает, почему JUnit 4 предоставляет assertEquals(foo,bar)
но нет assertNotEqual(foo,bar)
методы?
Это обеспечивает assertNotSame
(соответствует assertSame
) и assertFalse
(соответствует assertTrue
), поэтому кажется странным, что они не удосужились включить assertNotEqual
.
Кстати, я знаю, что JUnit-addons предоставляет те методы, которые мне нужны.Я просто спрашиваю из любопытства.
Решение
Я бы посоветовал вам использовать более новую 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.*;
Другие советы
Есть 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;
Мне интересно то же самое.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
Я бы сказал, что отсутствие AssertNotEqual действительно является асимметрией и делает JUnit немного менее обучаемым.Имейте в виду, что это аккуратный случай, когда добавление метода уменьшит сложность API, по крайней мере для меня:Симметрия помогает управлять большим пространством.Я предполагаю, что причина упущения может заключаться в том, что слишком мало людей используют этот метод.Тем не менее, я помню время, когда даже AssertFalse не существовало;следовательно, у меня есть позитивное ожидание, что этот метод в конечном итоге может быть добавлен, учитывая, что он несложный;хотя я признаю, что существует множество обходных путей, даже элегантных.
Я прихожу на эту вечеринку довольно поздно, но обнаружил, что форма:
static void assertTrue(java.lang.String message, boolean condition)
можно заставить работать в большинстве случаев «не равно».
int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;
Я работаю над JUnit в среде Java 8, используя jUnit4.12.
для меня:компилятор не смог найти метод AssertNotEquals, даже когда я использовал
import org.junit.Assert;
Поэтому я изменил assertNotEquals("addb", string);
кAssert.assertNotEquals("addb", string);
Итак, если вы столкнулись с проблемой относительно assertNotEqual
не распознан, затем измените его на Assert.assertNotEquals(,);
это должно решить вашу проблему
Очевидная причина, по которой люди хотели использовать метод AssertNotEquals(), заключалась в том, чтобы сравнивать встроенные функции без необходимости предварительного преобразования их в полноценные объекты:
Подробный пример:
....
assertThat(1, not(equalTo(Integer.valueOf(winningBidderId))));
....
против.
assertNotEqual(1, winningBidderId);
К сожалению, поскольку Eclipse по умолчанию не включает JUnit 4.11, вам придется быть подробным.
Предостережение. Я не думаю, что «1» нужно заключать в Integer.valueOf(), но, поскольку я только что вернулся из .NET, не рассчитывайте на мою правильность.
Лучше использовать Hamcrest для отрицательных утверждений, а не AssertFalse, так как в первом случае в отчете о тестировании будет показана разница в случае неудачного утверждения.
Если вы используете AssertFalse, вы просто получите сообщение об ошибке утверждения в отчете.то естьпотеря информации о причине сбоя.
Я полностью согласен с точкой зрения ОП. 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, age=10, FavoriteFood=сено]
в org.junit.Assert.fail(Assert.java:88)
Хорошо, объекты не равны.Но где проблема?
Какое поле в тестируемом методе оценивается неправильно?Один ?Два ?Все они ?
Чтобы его обнаружить, вам придется покопаться в createWithNewNameAndAge()
реализацию/использование отладчика, в то время как API тестирования был бы гораздо более дружелюбным, если бы он позволял нам различать ожидаемое и полученное.
JUnit 4.11 в качестве средства запуска тестов и API-интерфейс Test Matcher в качестве инструмента утверждения.
Здесь тот же сценарий тестирования, но в нем используется 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:
Ожидание:
<["скуби", 10, "сено"]>
содержать точно (и в том же порядке):
<["маленький скуби", 1, "сено"]>
но некоторые элементы не были найдены:
<["маленький скуби", 1]>
и других не ожидалось:
<["Скуби", 10]>
в 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-интерфейса сопоставления, безусловно, гораздо более понятно и гибко.
Согласованность API по модулю, почему JUnit не предоставил assertNotEquals()
по той же причине JUnit никогда не предоставлял такие методы, как
assertStringMatchesTheRegex(regex, str)
против.assertStringDoesntMatchTheRegex(regex, str)
assertStringBeginsWith(prefix, str)
против.assertStringDoesntBeginWith(prefix, str)
то естьнет конца предоставлению конкретных методов утверждения для тех вещей, которые вам могут понадобиться в вашей логике утверждений!
Гораздо лучше предоставить составные тестовые примитивы, такие как equalTo(...)
, is(...)
, not(...)
, regex(...)
и вместо этого позвольте программисту собрать их вместе для большей читабельности и здравомыслия.