Статьи

OSGi Service Test Helper: ServiceRegistrationRule

Сервисные тесты 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 .