Статьи

Совместное использование AssertJ и Awaitility благодаря Java 8 и лямбдам

AssertJ и Awaitility — два из моих любимых инструментов, используемых в автоматическом тестировании кода. К сожалению, до недавнего времени не было возможности использовать его вместе. Но затем в игру вошла Java 8, и нескольких десятков строк кода было достаточно, чтобы это произошло в Awaility 1.6.0.

awaitility_logo_red_small

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 .