Статьи

Тестирование асинхронного кода с потоками данных GPars

В своем последнем посте я показал, как использовать 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/')

Если вам нужна дополнительная помощь, посмотрите полный пример .

Счастливого тестирования!

С http://hamletdarcy.blogspot.com