AssertJ и Awaitility — два из моих любимых инструментов, используемых в автоматическом тестировании кода. К сожалению, до недавнего времени не было возможности использовать его вместе. Но затем в игру вошла Java 8, и нескольких десятков строк кода было достаточно, чтобы это произошло в Awaility 1.6.0.
AssertJ предоставляет богатый набор утверждений с очень полезными сообщениями об ошибках, все они доступны через API, свободно понимающий тип. Функция Awaitility позволяет выразить ожидания асинхронных вызовов в краткой и удобной для чтения форме, используя активный шаблон ожидания, который сокращает продолжительность тестов (не более sleep (5000)!).
Идея использовать его вместе пришла мне в голову год назад, когда я работал над проектом по трейдингу с использованием сложной обработки событий (CEP), и мне не нравилось изучать утверждение Hamcrest только для асинхронных тестов с Awaitility. Мне удалось создать работающий PoC , но для этого потребовалось значительное дублирование в коде AssertJ (тогда FEST Assert), и я отложил эту идею. Месяц назад я готовил презентацию по тестированию асинхронного кода для конференции 4Developers и задавал себе вопрос: как Java 8 может упростить использование Awaitility?
В нескольких примерах я буду использовать asynchronousMessageQueue
который можно использовать для отправки запроса ping и возврата количества полученных пакетов. Один из способов протестировать его с Awaitility в Java 7 (помимо условия на основе прокси) — создать Callable
класса Callable
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@Test public void shouldReceivePacketAfterWhileJava7Edition() { //when asynchronousMessageQueue.sendPing(); //then await().until(receivedPackageCount(), equalTo( 1 )); } private Callable<Integer> receivedPackageCount() { return new Callable<Integer>() { @Override public Integer call() throws Exception { return asynchronousMessageQueue.getNumberOfReceivedPackets(); } }; } |
где equalTo()
является стандартным сопоставителем Hamcrest.
Первая идея для сокращения многословия — заменить Callable
лямбда-выражением и Callable
приватный метод:
1
2
3
4
5
6
7
|
@Test public void shouldReceivePacketAfterWhile() { //when asynchronousMessageQueue.sendPing(); //then await().until(() -> asynchronousMessageQueue.getNumberOfReceivedPackets(), equalTo( 1 )); } |
Намного лучше. В дальнейшем лямбда-выражение можно заменить ссылкой на метод:
1
2
3
4
5
6
7
|
@Test public void shouldReceivePacketAfterWhile() { //when asynchronousMessageQueue.sendPing(); //then await().until(asynchronousMessageQueue::getNumberOfReceivedPackets, equalTo( 1 )); } |
Кто-то может пойти еще дальше и убрать совпадение Hamcrest:
1
2
3
4
5
6
7
|
@Test public void shouldReceivePacketAfterWhile() { //when asynchronousMessageQueue.sendPing(); //then await().until(() -> asynchronousMessageQueue.getNumberOfReceivedPackets() == 1 ); //poor error message } |
но пока он работает, сообщение об ошибке становится гораздо менее значимым:
1
2
|
ConditionTimeoutException: Condition with lambda expression in AwaitilityAsynchronousShowCaseTest was not fulfilled within 2 seconds. |
вместо очень ясно
1
2
|
ConditionTimeoutException: Lambda expression in AwaitilityAsynchronousShowCaseTest that uses AbstractMessageQueueFacade: expected <1> but was <0> within 2 seconds.> |
Решением было бы использовать утверждение AssertJ внутри лямбда-выражения:
1
2
3
4
5
6
7
|
@Test public void shouldReceivePacketAfterWhileAssertJEdition() { //when asynchronousMessageQueue.sendPing(); //then await().until(() -> assertThat(asynchronousMessageQueue.getNumberOfReceivedPackets()).isEqualTo( 1 )); } |
и благодаря новому условию AssertionCondition, которое было взломано в течение нескольких минут, оно стало реальностью в Awaitility 1.6.0. Конечно, свободный интерфейс AssertJ и значимые сообщения об ошибках для разных типов данных сохраняются.
В качестве бонуса все выражения, которые выдают AssertionError (в частности, стандартные утверждения TestNG и JUnit), могут также использоваться в лямбда-выражении (но я не знаю никого, кто вернулся к «стандартному» утверждению, зная силу AssertJ).
Приятно то, что сами изменения используют класс Runnable для реализации поддержки lambdas и AssertJ, а Awaitility 1.6.0 по-прежнему совместим с Java 5. Тем не менее, для удобства чтения целесообразно использовать новые конструкции в проектах на основе Java 8.
Кстати, вот « слайды » из моей презентации на 4Developers .
Ссылка: | Совместное использование AssertJ и Awaitility благодаря Java 8 и лямбдам от нашего партнера по JCG Марцина Заячковского в блоге Solid Soft . |