Сервисные тесты OSGi могут быть эффективным средством, чтобы избежать проблем, связанных с висящими ссылками на сервисы. Как и было обещано в моем посте о написании простых проверок вклада сервисов , на этот раз я ввожу правило JUnit, которое помогает тестировать взаимодействия между компонентами.
Сервисные тесты OSGi для взаимодействия компонентов
Предположим, у нас есть сервис, который уведомляет связанных наблюдателей, привязанных по шаблону доски . Именно у нас есть Service
и ServiceImpl
как в предыдущем посте. Кроме того, мы поддерживаем ServiceListener
который должен быть уведомлен о конкретных действиях.
Чтобы представить такое действие, мы расширили интерфейс службы нашего примера объявлением метода Service#execute()
:
1
2
3
|
public interface Service { void execute(); } |
Помимо реализации этого метода execute
класс вклада должен предоставлять возможности для привязки и отмены привязки ссылок ServiceListener
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public class ServiceImpl implements Service { public void execute() { [...] } public void bind( ServiceListener listener ) { [...] } public void unbind( ServiceListener listener ) { [...] } } |
В качестве пункта назначения уведомления тип обратного вызова ServiceListener
s предоставляет объявление метода с именем ServiceListener#executed()
:
1
2
3
|
public interface ServiceListener { void executed(); } |
Для завершения настройки нам необходимо зарегистрировать сервисный компонент, что мы снова делаем через декларативные сервисы . Обратите внимание на дополнительное объявление 0..n:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<? xml version = "1.0" encoding = "UTF-8" ?> < scr:component immediate = "true" name = "Implementation of Service API" > < implementation class = "com.codeaffine.example.core.ServiceImpl" /> < service < <provide interface = "com.codeaffine.example.api.Service" /> </ service > < reference bind = "bind" unbind = "unbind" cardinality = "0..n" interface = "com.codeaffine.example.api.ServiceListener" name = "ServiceListener" policy = "dynamic" /> </ scr:component > |
Теперь возникает вопрос: как мы можем проверить, что отмена / привязка слушателя работает правильно и уведомления отправляются, как и ожидалось? Основная идея состоит в том, чтобы зарегистрировать шпион ServiceListener
и инициировать выполнение Service#execute
на фактической реализации сервиса.
Шпион записывает вызовы для execute
и позволяет убедиться, что привязка и уведомление работают должным образом. Убедившись в этом, мы можем продолжить и отменить регистрацию в основном зарегистрированного шпиона и убедиться, что он не уведомлен о последующем событии действия. Это гарантирует, что открепление работает также, как запланировано.
Однако тестовое устройство для этого сценария, как правило, нуждается в небольшом шаблоне OSGi. Чтобы уменьшить беспорядок, я написал небольшое правило JUnit, которое облегчает регистрацию служб и автоматически выполняет очистку реестра служб после каждого запуска теста.
ServiceRegistrationRule
Как и любая другая JUnit TestRule
должна быть предоставлена как открытое поле в нашем тесте PDE . Обратите внимание, как правило использует параметризованный конструктор для данного экземпляра класса тестового примера. Эта ссылка используется для получения соответствующего BundleContext
для удаления / регистрации услуги.
01
02
03
04
05
06
07
08
09
10
11
12
|
@Rule public final ServiceRegistrationRule serviceRegistration = new ServiceRegistrationRule( getClass() ); private ServiceListener listener; private Service service; @Before public void setUp() { service = collectServices( Service. class , ServiceImpl. class ).get( 0 ); listener = mock( ServiceListener. class ); } |
Установка неявного теста извлекает зарегистрированный тестируемый сервис, используя ServiceCollector
я представил в последнем посте . DOC слушателя создается как шпион с использованием mockito . Первый описанный выше сценарий тестирования выглядит следующим образом:
1
2
3
4
5
6
7
8
|
@Test public void executeNotification() { serviceRegistration.register( ServiceListener. class , listener ); service.execute(); verify( listener ).executed(); } |
Довольно прямо, не так ли?
Обратите внимание, что ServiceRegistrationRule
заботится об очистке и удаляет шпионскую службу из реестра служб. Чтобы упростить тестирование сценария отмены привязки, метод register
правила возвращает дескриптор регистрации службы:
01
02
03
04
05
06
07
08
09
10
|
@Test public void executeAfterListenerRemoval() { Registration registration = serviceRegistration.register( ServiceListener. class , listener ); registration.unregister(); service.execute(); verify( listener, never() ).executed(); } |
Строка пять ( registration.unregister()
) удаляет шпиона слушателя из реестра службы. Это вызывает отмену привязки, и слушатель никогда не вызывается. Конечно, реальный сценарий может добавить дополнительные тесты для регистрации нескольких слушателей, обработки исключений и тому подобного, но я думаю, что концепция прояснена.
Вывод
До сих пор ServiceRegistrationRule
доказала свою эффективность в нашем текущем проекте. Это значительно снижает количество шаблонов, делает тесты чище и повышает удобочитаемость. Класс является частью функции com.codeaffine.osgi.test.util репозитория Xiled P2: http://fappel.github.io/xiliary
Если вы хотите взглянуть на код или подать проблему, вы также можете взглянуть на проект Xiled GitHub: https://github.com/fappel/xiliary
Для всего остального не стесняйтесь использовать раздел комментариев ниже. Далее я объясню, как настроить сборку maven-tycho со встроенными тестами PDE, как описано выше. Это несколько сложно, так как Tycho не позволяет получить доступ к пучкам, собранным текущим реактором, так что следите за обновлениями.
Ссылка: | Помощник по тестированию службы OSGi: ServiceRegistrationRule от нашего партнера по JCG Рудигера Херрманна в блоге Code Affine . |