Статьи

Разработка дополнений Roo — Как выполнить модульное тестирование изменений конфигурации

Я работаю над обновлениями для нескольких надстроек Roo, которые скоро собираюсь выпустить в репозиторий Roo. Вот некоторые проблемы и как я их преодолел.

Интерфейсный маркерный интерфейс команды

Это дополнение устанавливает Coffeescript с помощью плагина Maven. Вот источник дополнения:

package org.sillyweasel.addons.coffeescript;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.shell.CliAvailabilityIndicator;
import org.springframework.roo.shell.CliCommand;
import org.springframework.roo.shell.CliOption;
import org.springframework.roo.shell.CommandMarker;

@Component
@Service
public class CoffeescriptCommands implements CommandMarker { 

  /**
   * Get a reference to the CoffeescriptOperations from the underlying OSGi
   * container
   */
  @Reference
  CoffeescriptOperations operations;

  @CliAvailabilityIndicator({"coffeescript setup"})
  public boolean isSetupCommandAvailable() {
    return operations.isSetupCommandAvailable();
  }

  @CliAvailabilityIndicator({"coffeescript remove", "coffeescript addjoinset", 
    "coffeescript removejoinset", 
    "coffeescript listjoinsets"})
  public boolean isCoffeescriptInstalled() {
    return operations.isPluginInstalled();
  }

  @CliCommand(value = "coffeescript setup", 
     help = "Install the CoffeeScript compiler")
  public void setup(
    @CliOption(key = "outputDirectory", mandatory = false,
        unspecifiedDefaultValue = "${project.build.directory}") String outputDirectory,
    @CliOption(key = "coffeeDir", mandatory = false,
        unspecifiedDefaultValue = "src/main/webapp/scripts",
        specifiedDefaultValue = "src/main/webapp/scripts") String coffeeDir,
    @CliOption(key = "bare", mandatory = false,
        unspecifiedDefaultValue = "false",
        specifiedDefaultValue = "true") boolean bare) {

    operations.setup(coffeeDir, outputDirectory, bare);
  }

  public void addJoinSet(
      @CliOption(key = "joinSetId", mandatory = true, specifiedDefaultValue = "main")
      String joinSetId,
      @CliOption(key = "includes", mandatory = true, 
         help = "comma-separated list of search paths for javascript files to include")
      String includes,
      @CliOption(key = "excludes", mandatory = true, 
         help = "comma-separated list of search paths for javascript files to exclude")
      String excludes) {

    // TODO - set up the command

  }

  @CliCommand(value = "coffeescript remove", help = "Remove the coffeescript compiler")
  public void remove() {
    operations.remove();
  }
}

Кое-что из этого я еще не написал (отсюда и TODO ), а остальное уточняется, и я расскажу об этом позже.

Задача № 1 — аддоны Roo не включают JUnit?

Прямо сейчас нет. ( ROO -3161 ) Тем не менее, вы можете просто добавить его yerself. Я использую Mockito для насмешек (подробнее об этом позже), а также бездельничаю с этими прекрасными матчами Hamcrest:

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.10</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-all</artifactId>
  <version>1.1</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-all</artifactId>
  <version>1.9.0</version>
  <scope>test</scope>
</dependency>

Задача № 2 — Вам нужно расширить видимость объектов OSG i, введенных Roo, чтобы их высмеивать

Поскольку мы хотим выполнить модульное тестирование наших надстроек, не запуская контейнер OSG i, нам нужно будет что-то делать. Я использую Mockito , отличную библиотеку для шуток и насмешек . Но я не могу добраться до внедренных объектов, и тест, который я собираюсь показать здесь, должен сделать «издевательство» над службой ProjectOperations Roo. Это определяется так в коде класса реализации Operations:

private @Reference ProjectOperations projectOperations;

Проблема в том, что я не могу просто использовать метод Mockito для получения доступа и фальсификации. Итак, я расширился до дружественной области, которая позволяет получить доступ из одного пакета. Я полагаю, что лучшим способом было бы создать геттер, но тогда я представляю его в более широком диапазоне, чем просто конкретный пакет дополнения:

@Reference ProjectOperations projectOperations;

Задача № 3 — как проверить маркер команды

Итак, первое, что я хочу сделать, это убедиться, что мой Command Marker вызывает правильные методы реализации команд. Сначала расширим ссылку на нашу переменную CoffeescriptOperations:

@Reference CoffeescriptOperations operations;

Затем мы пишем метод, который использует Mockito для имитации вызовов к объекту операций. Это тестовый класс:

package org.sillyweasel.addons.coffeescript;

import junit.framework.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.*;

public class CoffeescriptCommandsTest {

  private CoffeescriptCommands commands;

  @Before
  public void setUp() {
    commands = new CoffeescriptCommands();
    commands.operations = mock(CoffeescriptOperations.class);
  }

  @Test
  public void testIsPluginInstalled() {
    when(commands.operations.isPluginInstalled()).thenReturn(false);
    assertThat(commands.isCoffeescriptInstalled(), is(false));
  }

  @Test
  public void testIsCoffeescriptSetupAvailable() {
    when(commands.operations.isSetupCommandAvailable()).thenReturn(true);
    assertThat(commands.isSetupCommandAvailable(), is(true));
  }

  @Test
  public void testIsCoffeescriptRemoveAvailable() {
    when(commands.operations.isPluginInstalled()).thenReturn(true);
    assertThat(commands.isCoffeescriptInstalled(), is(true));
    verify(commands.operations, times(1)).isPluginInstalled();
  }

  @Test
  public void testInstallCoffeeScript() {
    commands.setup("foo", "bar", true);
    verify(commands.operations).setup("foo", "bar", true);
  }

  @Test
  public void testRemoveCoffeeScript() {
    commands.remove();
  }

  @Test
  @Ignore
  public void testFileSets() {
    Assert.fail();
  }
}

Видишь, как я использую там какие-нибудь спички Hamcrest? Мне нравится синтаксис assertThat (…, is ()).

Итак, теперь мы знаем, что наш маркер команды фактически вызывает методы делегата, когда он вызывается. И мы на пути к тому, чтобы смоделировать все, что захотим, при условии, что мы немного расширим область видимости из 100% внутренней закрытой переменной-члена.

В нашем следующем посте я покажу вам, как проверить фактическую конфигурацию XML . Этот будет довольно большим, поэтому я разделю его на другой.