Почему я должен использовать Hamcrest-Matcher и assertThat () вместо традиционных assertXXX () - Методы

Когда я смотрю на примеры в классе Assert JavaDoc

assertThat("Help! Integers don't work", 0, is(1)); // fails:
// failure message:
// Help! Integers don't work
// expected: is <1> 
// got value: <0>
assertThat("Zero is one", 0, is(not(1))) // passes

Я не вижу большого преимущества перед, скажем, assertEquals( 0, 1 ).

Может быть, это хорошо для сообщений, если конструкции усложняются, но видите ли вы больше преимуществ? Читаемость?


person Community    schedule 09.11.2009    source источник


Ответы (7)


Нет большого преимущества в тех случаях, когда существует assertFoo, который точно соответствует вашему намерению. В этих случаях они ведут себя почти одинаково.

Но когда вы переходите к более сложным проверкам, преимущество становится более заметным:

val foo = List.of("someValue");
assertTrue(foo.contains("someValue") && foo.contains("anotherValue"));
Expected: is <true>
         but: was <false>

vs.

val foo = List.of("someValue");
assertThat(foo, containsInAnyOrder("someValue", "anotherValue"));
Expected: iterable with items ["someValue", "anotherValue"] in any order
     but: no item matches: "anotherValue" in ["someValue"]

Можно обсудить, какой из них легче читать, но как только утверждение не сработает, вы получите хорошее сообщение об ошибке от assertThat, но только очень минимальный объем информации от assertTrue.

person Joachim Sauer    schedule 09.11.2009
comment
У меня тоже был этот вопрос в глубине души. Спасибо, я никогда не думал об этом в таком ключе. - person wheaties; 09.11.2009
comment
Это также помогает с правилом одного утверждения для каждого теста и легче сочетается со спецификациями в стиле BDD. - person Nils Wloka; 20.11.2009
comment
И он отделяет механизм утверждения от условия (что приводит к более качественным сообщениям об ошибках). - person SteveD; 30.08.2010
comment
Пример неправдоподобен, так как вряд ли кто-нибудь будет использовать один assertTrue с &&. Разделение на два условия делает причину проблемы очевидной даже в JUnit. Не поймите меня неправильно; Я согласен с вами, мне просто не нравится ваш пример. - person maaartinus; 11.08.2017

В примечаниях к выпуску JUnit для версии 4.4 (где она была представлена) говорится о четырех преимуществах:

  • Более читабельный и печатный: этот синтаксис позволяет вам мыслить в терминах субъекта, глагола, объекта (assert «x is 3»), а не assertEquals, который использует глагол, объект, субъект (assert «равно 3 Икс")
  • Комбинации: любой оператор сопоставления s может быть инвертирован (not (s)), объединен (or (s) .or (t)), сопоставлен с коллекцией ( каждый) или используется в пользовательских комбинациях (afterFiveSeconds (s))
  • Читаемые сообщения об ошибках. (...)
  • Пользовательские сопоставители. Реализуя интерфейс Matcher самостоятельно, вы можете получить все вышеперечисленные преимущества для своих собственных пользовательских утверждений.

Более подробная аргументация автора нового синтаксиса: здесь.

person Manur    schedule 06.04.2010

В основном для повышения читабельности кода.

Помимо hamcrest, вы также можете использовать fest утверждения. У них есть несколько преимуществ перед хамкрестом, например:

Некоторые примеры

import static org.fest.assertions.api.Assertions.*;

// common assertions
assertThat(yoda).isInstanceOf(Jedi.class);
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);
assertThat(frodo).isIn(fellowshipOfTheRing);
assertThat(sauron).isNotIn(fellowshipOfTheRing);

// String specific assertions
assertThat(frodo.getName()).startsWith("Fro").endsWith("do")
                           .isEqualToIgnoringCase("frodo");

// collection specific assertions
assertThat(fellowshipOfTheRing).hasSize(9)
                               .contains(frodo, sam)
                               .excludes(sauron);


// map specific assertions (One ring and elves ring bearers initialized before)
assertThat(ringBearers).hasSize(4)
                       .includes(entry(Ring.oneRing, frodo), entry(Ring.nenya, galadriel))
                       .excludes(entry(Ring.oneRing, aragorn));

Обновление от 17 октября 2016 г.

Fest больше не активен, используйте вместо него AssertJ.

person Igor Popov    schedule 13.10.2012
comment
Фест вроде бы умер, но форк AssertJ жив. - person Amedee Van Gasse; 17.02.2016

Основное оправдание состоит в том, что новый синтаксис сложно испортить.

Предположим, что конкретное значение foo после проверки должно быть равно 1.

assertEqual(1, foo);

--OR--

assertThat(foo, is(1));

При первом подходе очень легко забыть правильный порядок и ввести его в обратном порядке. Тогда вместо того, чтобы сказать, что тест не прошел, потому что он ожидал 1 и получил 2, сообщение обратное. Не проблема, когда тест проходит успешно, но может привести к путанице, если тест не пройден.

Во второй версии сделать эту ошибку практически невозможно.

person Andy Davis    schedule 28.02.2012
comment
... и когда Eclipse сообщает об ошибке утверждения, если вы неправильно разместите аргументы в традиционном assertThat (), ошибка не будет иметь смысла. - person Sridhar Sarnobat; 14.04.2016

Пример:

assertThat(5 , allOf(greaterThan(1),lessThan(3)));
//  java.lang.AssertionError:
//  Expected: (a value greater than <1> and a value less than <3>)
//       got: <5>
assertTrue("Number not between 1 and 3!", 1 < 5 && 5 < 3);
//  java.lang.AssertionError: Number not between 1 and 3!
  1. вы можете сделать свои тесты более конкретными
  2. вы получите более подробное исключение, если тесты не пройдут
  3. легче читать тест

Кстати: вы тоже можете писать текст в assertXXX ...

person MartinL    schedule 31.10.2011
comment
Еще лучше, я бы оставил строковый аргумент в случае assertThat, потому что сообщение, которое вы получаете автоматически, столь же информативно: Ожидаемое: (значение больше ‹1› и значение меньше ‹3›) - person MatrixFrog; 01.11.2011
comment
Да ты прав. Редактирую свой ответ. Изначально я хотел использовать оба (Matcher) и (Matcher), но это не сработало. - person MartinL; 02.11.2011

assertThat(frodo.getName()).isEqualTo("Frodo");

Близок к естественному языку.

Легче читать, легче анализировать код. Программист тратит больше времени на анализ кода, чем на написание нового. Так что, если код будет легко анализировать, разработчик должен работать более продуктивно.

P.S. Код должен быть как хорошо написанная книга. Самодокументированный код.

person user4515828    schedule 31.01.2015
comment
Хорошо и…? Я рекомендую подкрепить ваш аргумент, объяснив, почему это хорошо. - person Nathan Tuggy; 01.02.2015

есть преимущества assertThat по сравнению с assertEquals -
1) более читабельный
2) больше информации об ошибках
3) ошибки времени компиляции - а не ошибки времени выполнения
4) гибкость при написании условий тестирования
5) переносимый - если вы используете hamcrest - вы можете использовать jUnit или TestNG в качестве базовой структуры.

person samshers    schedule 24.09.2017