Статьи

Координация асинхронных юнит-тестов с JConch 1.2

Медленно, но верно JConch Java Concurrency Library становится базой для многопоточного и асинхронного тестирования на платформе Java. Сначала был SerialExectorService , позволяющий вам тестировать с Java ExecutorService, который никогда не запускал потоки ( например , javadoc ). Затем был assertSynchronized , позволяющий вам легко выполнять модульные тестовые утверждения о политике синхронизации вашего объекта ( javadoc , пример ).

И теперь JConch 1.2 предлагает TestCoordinator: инструмент для тестирования асинхронного кода и многопоточных обратных вызовов. TestCoordinator решает проблему правильного ожидания асинхронных вызовов методов без засорения ваших модульных тестов вызовами wait / join или вызовами API CyclicBarrier / CountDownLatch. В связи с этим он предоставляет полезную абстракцию модульного тестирования для больших примитивов параллелизма Java 5.

Примечание. Если вы хотите пропустить всю прозу, перейдите непосредственно к примеру и модульным тестам, которые являются частью JConch 1.2.

Написание хороших модульных тестов требует того же ввода, что и написание хорошего производственного кода: практика, самоотдача и около 10 000 часов.час опыта. Практически любой компонентный каркас, для которого требуется какой-либо прослушиватель событий, всегда имеет модульный тест, который выглядит следующим образом

MyComponent component = new MyComponent()

ActionEvent event
component.addActionListener({ ActionEvent e ->
event = e
} as ActionListener)

component.click()
Thread.sleep(1000)

assert event.source != null
assert "click" == event.actionCommand

Хорошо, обычно это на Java, а не на Groovy, но вы понимаете. Жизнь слишком коротка, чтобы писать Java дома. Простой формат: 1) создать компонент с прослушивателем, который захватывает входные данные, 2) активировать компонент и 3) перевести поток в спящий режим на некоторое время и надеяться, что событие сработает до запуска ваших утверждений. Очевидная проблема заключается в том, что вы в конечном итоге получаете медленные или периодически проваливающиеся модульные тесты в зависимости от того, как долго вы спите (нет, на самом деле не существует идеального числа сна, которое избегает обоих).

В Java 4 дня это было «решено» с помощью вызовов методов Object # wait и Object # notify. Ewww. Java 5 представила CyclicBarrier и CountDownLatch, которые хорошо решили проблему, хотя и примитивно. Это улучшение,но мы можем сделать лучше, чем это:

@Test(timeout=5000)
public void testClick() {
CyclicBarrier gate = new CyclicBarrier(2)

MyComponent component = new MyComponent()

component.addActionListener({ ActionEvent e ->
assert "click" == e.actionCommand
gate.await()
} as ActionListener)

component.click()
gate.await()
}

Этот подход позволяет нам перемещать методы утверждения внутри обратного вызова, потому что мы застрахованы, что либо будет вызван обратный вызов, либо тест завершится неудачно с таймаутом (бит @Test (timeout = 5000)). Это проще и скрывает некоторую сложность, но все еще есть куча примитивов, отвлекающих читателя от основного содержимого теста … что это за параметр (2) в конструкторе барьера? Значение времени ожидания ясно или оно скрыто в аннотации метода? И что же означает await ()? Здесь много примитивности, над которой абстрагируется TestCoordinator:

import jconch.testing.TestCoordinator

TestCoordinator coord = new TestCoordinator()

MyComponent component = new MyComponent()
component.addActionListener({ ActionEvent e ->
assert "click" == e.actionCommand
coord.finishTest()
} as ActionListener)

component.click()
coord.delayTestFinish(1, TimeUnit.SECONDS)

 Как вы можете видеть, TestCoordinator действует как барьер или защелка, но без всего API низкого уровня. Когда вы активировали тестируемый класс, вы вызываете delayTestFinish (…); модульный тест будет ожидать модуля, который слушатель вызывает finishTest (). И если метод finalTest () уже был вызван (это часто встречается в многопоточных системах), то delayTestFinish просто продолжается. Если значение тайм-аута истекает, тест не пройден Нет смешной аннотации тайм-аута. Нет смешного API. Просто простой координатор. Вы хотите отложить тест до завершения обратного вызова, и API разработан вокруг этой номенклатуры.

Есть и два других варианта задержки:

coord.delayTestFinish(1000) // milliseconds
coord.delayTestFinish() // requires @Timeout value!

Для тех из вас, кто пришел из GWT … да, это почти точно API GWTTestCase . Используйте то, что работает, я говорю.

Для получения дополнительной информации о TestCoordinator, проверьте UnitTest и пример . Счастливого тестирования!

С http://hamletdarcy.blogspot.com