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


153

Когда я смотрю на примеры в классе 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 ).

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

Ответы:


173

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

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

assertTrue(foo.contains("someValue") && foo.contains("anotherValue"));

против

assertThat(foo, hasItems("someValue", "anotherValue"));

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

assertThatскажет вам, что было утверждение и что вы получили вместо этого. assertTrueскажу только то, что вы получили falseтам, где ожидали true.


У меня тоже был этот вопрос в глубине души. Спасибо, я никогда не думал об этом таким образом.
Wheaties

1
Это также помогает с «правилом» одного утверждения на тест и легче сочетается со спецификациями в стиле BDD.
Нильс Влока

2
И это отделяет механизм утверждения от условия (что приводит к лучшим сообщениям об ошибках).
SteveD

2
Этот пример неправдоподобен, так как вряд ли кто-то использовал бы сингл assertTrueс &&. Разделение его на два условия делает проблему очевидной даже в JUnit. Не пойми меня неправильно; Я согласен с вами, мне просто не нравится ваш пример.
Maaartinus

48

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

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

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


39

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

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

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

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 .


4
Фест, кажется, умер, но форк AssertJ очень жив.
Амеди Ван Гассе

18

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

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

assertEqual(1, foo);

--ИЛИ--

assertThat(foo, is(1));

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

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


... и когда Eclipse сообщает об ошибке подтверждения, если вы неправильно указали аргументы в традиционной assertThat (), ошибка не имеет смысла.
Шридхар Сарнобат

9

Пример:

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 тоже ...


1
Еще лучше, я бы пропустил строковый аргумент в этом assertThatслучае, потому что сообщение, которое вы получаете автоматически, столь же информативно: «Ожидаемое: (значение больше <1> и значение меньше <3>)»
MatrixFrog

Да ты прав. Я редактирую свой ответ. Первоначально я хочу использовать оба (Matcher). И (Matcher), но это не сработало.
MartinL

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

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

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

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


4
Хорошо и…? Я рекомендую поддержать ваш аргумент, объяснив, почему это хорошо.
Натан Тагги

0

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.