В своем последнем посте я показал, как использовать JConch 1.2 для модульного тестирования асинхронного кода. Он содержит механизм блокировки / барьера, который позволяет вам изящно сказать вашему модульному тесту, как ждать и переходить к событиям вне потока без ожидания или опроса. Whee!
В качестве небольшого примера, вот как 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)
Если вы живете в мире Groovy, вам следует рассмотреть возможность использования переменных потока данных GPars вместо JConch TestCoordinator. Преимущество использования потоков данных заключается в том, что вы получаете аналогичный API координации потоков, но также и API захвата переменных, позволяющий перемещать операторы утверждений в конец вашего модульного теста, где они принадлежат. Если вы ищете чистый, минималистский подход к записи событий потоков, то потоки данных могут быть именно тем, что вам нужно:
import java.awt.event.*
import javax.swing.SwingUtilities
import groovyx.gpars.dataflow.DataFlowVariable
def event = new DataFlowVariable()
MyComponent component = new MyComponent()
component.addActionListener({ ActionEvent e ->
event << e
} as ActionListener)
component.click()
ActionEvent e = event.val
assert e.source != null
assert e.actionCommand == "click"
DataFlowVariable немного похож на Java Future, смешанный с неизменным, потокобезопасным объектом. DataFlowVariable имеет базовое значение, которое может быть установлено один раз и только один раз, и попытка получить доступ к этому значению заблокирует вызывающую сторону, пока значение не станет доступным. В этом примере установка значения выполняется в обработчике событий с помощью строки «event << e», а доступ к значению осуществляется в строке «event.val». Получение «val» будет блокировать вызывающего до тех пор, пока значение не станет доступным. (Помните, что в Groovy доступ к свойству .val преобразуется в вызов метода getVal ()).
Преимущество использования потоков данных в тестировании по сравнению с Java заключается в том, что вы получаете более приятный API-интерфейс для синхронизации потоков, чем тот, который предоставляют примитивы java.util.concurrent. Преимущество потоков данных по сравнению с JConch состоит в том, что вы можете перенести методы подтверждения обратно в конец модульных тестов. Уильям Уэйк описал формат для модульных тестов, называемый Arrange-Act-Assert , и он по-прежнему является одним из лучших руководств для написания понятных и понятных модульных тестов. Методы утверждения принадлежат в конце вашего теста, а код размещения — в начале (обратите внимание, сколько фреймворков фиктивных объектов нарушает этот порядок!). Захват переменной — это хороший способ перенести утверждения в конец метода, но в большинстве сред требуется большое количество случайно сложного кода.
Недостатком использования Dataflows является то, что вам снова нужно установить значение @Timeout в модульном тесте в случае, если вы никогда не получите значение (иначе ваши тесты будут зависать). ОЙ, ПОДОЖДИТЕ, это совершенно неправильно. Авторы GPars предоставили значение тайм-аута в DataFlowVariable # getVal ():
ActionEvent e = event.getVal(4, TimeUnit.SECONDS)
Я был там на секунду, правда? Имейте в виду, что если превышено значение тайм-аута, то getVal () возвращает ноль, исключение не выдается.
Единственный недостаток использования параллелизма DataFlow, который я вижу, заключается в том, что вам действительно нужно немного понять о параллелизме DataFlow. Кто бы мог подумать? DataFlowVariable является лишь верхушкой айсберга, когда дело касается поддержки GPars DataFlow, и даже не является основным вариантом использования. Руководство пользователя по потоку данных хорошо читается и в нем будет много объяснено больше понятий и реализации GPars.
Не стесняйтесь попробовать это самостоятельно. Вам даже не нужно загружать какие-либо банки или изменять файлы проекта. Просто добавьте правильный виноград в начало вашего юнит-теста:
@Grab(group='org.codehaus.gpars', module='gpars', version='0.9')
@GrabResolver(name='jboss', root='http://repository.jboss.org/maven2/')
Если вам нужна дополнительная помощь, посмотрите полный пример .
Счастливого тестирования!