Agiles Testen von JIRA Plugins (Teil 2): Wired Tests

Keine Kommentare

Im letzten Beitrag haben wir – das sind Raimar Falke und ich – einen Blick auf das Setup eines JIRA Plugin Projektes geworfen und wie man dafür Unittests aufsetzt und ausführt. In diesem Beitrag werden wir uns nun “Wired Tests”, einer weiteren Testart, widmen.

Hinweis: Dies ist eine 4-teilige blog Serie. Agiles Testen von JIRA Plugins (Teil 1), Wired Tests (Teil 2), Systemtests (Teil 3), CI Server Integration und Test Coverage (Teil 4). Darüber hinaus finden Sie auch eine Einführung zum Thema JIRA mit Plugins erweitern in unserem blog.

Wired Test

Atlassian bezeichnet mit dem Begriff “Wired Test” einen Test, welcher innerhalb der laufenden Host Application, in unserem Fall also JIRA, ausgeführt wird. Technisch gesehen werden die Testklassen als separates Test-Plugin zusammengefasst und in die laufende Host Application geladen. Ein spezieller Testrunner (AtlassianPluginsTestRunner) wird dann benötigt, welcher aus der VM heraus, in der JUnit läuft, das Test-Plugin aufruft und die Ausführung der Tests startet. Die Ergebnisse des Tests werden dann wieder an die JUnit-VM geschickt.Dieser Ansatz hat einige Vor- aber auch Nachteile. Zunächst die Vorteile:

  • Die Tests laufen in einer Umgebung, die sehr ähnlich dem späteren Produktionssystem ist.
  • Alle Abhängigkeiten können durch den Konstruktor der Testklasse einfach bereitgestellt werden.
  • Im Fehlerfall erhält man einen aussagekräftigen Stacktrace.

Es gibt jedoch auch einige Nachteile:

  • Der Start einer JIRA Instanz ist sehr langsam. Auf einem etwas schwächeren CI Server kann die Durchführung auch einfacher Tests durchaus 10 Minuten und mehr dauern.
  • Der Testrunner führt immer alle Tests der Testklasse aus, auch wenn man nur gezielt einen einzelnen Test ausführen will.
  • Wired Tests tragen nicht zur Code Coverage bei – zumindest nicht ohne einigen Aufwand. Das Thema Code Coverage werden wir in einem späteren Beitrag noch anreißen.
  • Man ist in der Wahl der Test-Abhängigkeiten nicht völlig frei. So muss man beispielsweise die mitgelieferte JUnit version (4.10) benutzen. Selbst wenn man explizit eine andere Version vorgibt, wird diese nicht in das Test-Plugin übernommen.
  • Im Normalfall wird die Host Application für mehr als einen Test benutzt. Das bedeutet natürlich, dass man besonders sorgfältig darauf achten muss, dass man den globalen Zustand (z.B. statische Variablen) nicht unabsichtlich verändert, und dass Tests mit Daten aus früheren Testläufen oder vorher gelaufenen Tests umgehen müssen.
  • Es gibt keine Möglichkeit, Abhängigkeiten für einen Test gezielt zu verändern (was ja bei Unittests mit Mocks möglich ist), sodass bspw. ausgehende Emails statt versandt zu werden, in einem Event-Log landen. Insofern sind Wired Tests mehr System- als Integrationstests.
  • Da die Tests als eigenes Plugin im OSGi Container laufen, können sie nur die tatsächlich exportierten Methoden des zu testendenen Plugins testen. Das bedeutet einerseits, dass die zu testende Komponente ein Interface implementieren muss, gegen das der Test läuft, und andererseits müssen auch alle Klassen, die im Interface referenziert werden, exportiert werden, da der OSGi ClassLoader sie sonst nicht kennt. Da dieser Export für die normale Interaktion innerhalb des Plugins nicht benötigt wird, benötigt das einen Eingriff in die Konfiguration des Plugins, die nur für den Test notwendig ist, aber global definiert wird

Außerdem gilt:

  • Es wird eine laufende JIRA Instanz benötigt. Wenn der Test in der Integrationstestphase durch Maven ausgeführt wird, geschehen Start und Stop der Instanz automatisch. Alternativ kann man die Instanz auch mittels “atlas-run” manuell gestartet werden. Die Verwendung einer normalen JIRA Installation ist nicht möglich, da diese einige Entwickler-Plugins nicht enthält.
  • Wenn man die Standardkonfiguration verwendet, müssen alle Wired Tests in einem Package mit Präfix it.* (it steht dabei für Integrationstest) liegen, damit die Atlassian Plugins sie korrekt ausführen.

Unsere Meinung

Die Liste der Nachteile scheint lang, was aber für einen derartig tief eingreifenden Test verständlich ist. Die wichtigere Frage ist aus unserer Sicht jedoch, wozu man Wired Tests benötigt. Wir vermuten, dass die Notwendigkeit aus der Tatsache entstand, dass ein Unittest sehr stark in der Interaktion mit anderen Komponenten eingeschränkt ist, wenn nicht die gesamte Host Application gestartet wurde. Ein zweiter Grund kann die Testbarkeit von Services sein, welche keine REST Schnittstelle bereitstellen, und daher nicht “von außen” getestet werden können. Eine Möglichkeit, die Geschäftslogik auch ohne die oben genannten Nachteile zu testen, besteht in der Erstellung einer Fassade, die die benutzten JIRA Funktionen kapselt, und dem Test der Geschäftslogik gegen einen Mock der Fassade. Auf diese Weise benötigt man keinen Wired Test. Trotzdem gibt es Fälle, in denen Wired Tests die beste Wahl sind.

Beispiel für einen Wired Test

Um Wired Tests auszuführen, ist es notwendig einen Plugin Descriptor für das Test-Plugin zu erstellen (src/test/resources/atlassian-plugin.xml). Wenn man die Atlassian Befehle zum Hinzufügen von Komponenten und Ressourcen verwendet, wird der Descriptor für das Test Plugin automatisch erstellt bzw. um die neuen Elemente ergänzt; ansonsten müssen diese Komponenten manuell nachgetragen werden.

Ein Beispiel für einen Plugin Descriptor könnte so aussehen (Foo ist das Interface der zu testenden Komponente):

<atlassian-plugin key="${project.groupId}.${project.artifactId}-tests" 
  name="${project.name}" plugins-version="2">
  <plugin-info>
    <description>${project.description}</description>
    <version>${project.version}</version>
    <vendor name="${project.organization.name}" url="${project.organization.url}"/>
  </plugin-info>
 
  <!-- from the product container -->
  <component-import key="applicationProperties" 
    interface="com.atlassian.sal.api.ApplicationProperties"/>
 
  <!-- from the plugin under test -->
  <component-import key="foo-component" interface="com.example.Foo"/>
</atlassian-plugin>

Der Test selbst ist dann quasi ein Unittest, mit dem Unterschied, dass dem Konstruktor alle Abhängigkeiten als Parameter übergeben werden und die Klasse mit dem erwähnten Testrunner annotiert wird:

@RunWith(AtlassianPluginsTestRunner.class)
 
public class FooWiredTest {
  private Foo component;
 
  public FooWiredTest(Foo component) {
    this.component = component;
  }
 
  @Test
  public void test_foo_saves() {
    component.setValue("myTestValue");
    assertEquals("myTestValue", component.getValue());
  }
}

Ausführen von Wired Tests

Wired Tests können als Teil des normalen Builds mit dem Kommando “atlas-integration-test” gestartet werden. Alternativ können auch normale Maven Befehle benutzt werden, die die Integrationstests anstoßen, bspw. “mvn integration-test” oder “mvn verify”. In diesen Fällen wird immer eine neue JIRA Instanz gestartet und Plugin sowie Test Plugin dort installiert.

Darüber hinaus können Wired Tests auch gegen eine bereits laufende JIRA Instanz ausgeführt werden, indem das Kommando “atlas-remote-test” benutzt wird. Dieses benötigt jedoch Parameter, die beschreiben, wo der Server läuft, gegen den getestet wird. Wenn man das Standard-Setup von JIRA verwendet, lautet das Kommando z.B. “atlas-remote-test –server localhost –http-port 2990 –context-path /jira”.

Um Wired Tests aus der IDE zu starten, muss ebenfalls zunächst eine JIRA Instanz gestartet werden (z.B. “atlas-run” oder “mvn jira:run”). Dies lädt auch gleich die aktuelle Version von Plugin und Test Plugin in die Testinstanz. Zusätzlich ist es notwendig, dem Test beim Start die System Property “baseurl” zu setzen, z.B. “-Dbaseurl=http://localhost:2990/jira”. Bei der Verwendung der Maven oder Atlassian Befehle wird diese Property automatisch durch das Atlassian SDK gesetzt.

Bei der Ausführung von Tests aus der IDE heraus muss man jedoch immer bedenken, dass die Tests in der laufenden JIRA Instanz und nicht der JVM der IDE ausgeführt werden. Alle Änderungen am Test (oder dem Plugin) müssen daher zunächst in die JIRA Testinstanz deployt werden, bevor die Tests wiederholt werden. Diese Aktualiserung kann durch das Kommando “atlas-install-plugin” erreicht werden, wodurch das eigentliche Plugin neu geladen wird, oder mittels “mvn package jira:install jira:test-install”, wodurch sowohl das Plugin als auch das Test Plugin neu geladen werden.

Zusammenfassung

Auch wenn das Konzept, Tests innerhalb der Host Application laufen zu lassen, theoretisch sehr verlockend ist, hat es uns in der Praxis nicht wirklich überzeugen können. Die schwerwiegendsten Nachteile sind dabei die Tatsache, dass die Tests nicht wirklich so unabhängig vom regulären Code sind, wie sie sein sollten, und dass das Einspielen neuer Plugin- bzw. Test-Plugin-Versionen nicht wirklich schnell oder einfach möglich sind.

Der nächste Beitrag wird sich jedoch zunächst den Frontend-Tests widmen.

Thomas Strecker

Thomas Strecker ist als Senior IT Consultant bei der codecentric AG tätig und leitet mit Marcel Wolf den Standort Berlin. Neben seiner Führungsarbeit beschäftigt er sich in Projekten weiterhin mit Themen wie Java Profiling und Performance-Analyse, Microservices oder auch Testautomatisierung im Kontext agiler Softwareentwicklung.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.