Last updated Oct 9, 2024

Writing integration tests for your JIRA add-on

Level of experience: Intermediate

Our tutorials are classified as 'beginner', 'intermediate' and 'advanced'. This one is at 'intermediate' level. If you have never developed a plugin before, you may find this one a bit difficult.

Overview

This tutorial shows you how to write integration tests for a plugin. This tutorial demonstrates how to write such tests for both UI elements provided by the plugin itself, as well as the host application.

In order to do this, you will create a JIRA plugin. As with all plugins, your plugin will consist of the following components:

  • Java classes encapsulating the plugin logic
  • Resources for display of the plugin UI
  • Plugin descriptor to enable the plugin module in JIRA.

All these components will be contained within a single JAR file. Each component is discussed further in the examples below.

There are various definitions available for what integration testing actually means. For the purposes of this tutorial, integration testing is defined as running a set of automated tests against the web-UI of your plugin, using HTTP requests and responses to assert that the web-UI is exhibiting the correct behaviour.

Plugin Source

We encourage you to work through this tutorial. If you want to skip ahead or check your work when you are done, you can find the plugin source code on Atlassian Bitbucket. Bitbucket serves a public Git repository containing the tutorial's code. To clone the repository, issue the following command:

1
2
$ git clone https://atlassian_tutorial@bitbucket.org/atlassian_tutorial/jira-integration-tests-plugin.git

Alternatively, you can download the source using the Downloads page here: bitbucket.org/atlassian_tutorial/jira-integration-tests-plugin

Step 1. Create the Plugin Project

Use the appropriate atlas-create-application-plugin command to create your plugin. For example, atlas-create-jira-plugin or atlas-create-confluence-plugin.

When you create the plugin using the above SDK, you will be prompted for some information to identify your plugin. Enter the following information:

  • group-id: com.atlassian.plugins.tutorial
  • artifact-id: jira-integration-tests-tutorial
  • version: 1.0
  • package: com.atlassian.plugins.tutorial

Step 2. Add Plugin Metadata to the POM

Now you need to edit your POM (Project Object Model definition file) to add some metadata about your plugin and your company or organisation.

  1. Edit the pom.xml file in the root folder of your plugin.

  2. Add your company or organisation name and your website to the <organization>element:

    1
    2
    <organization>
    <name>Example Company</name>
    <url>http://www.example.com/</url>
    </organization>
    
  3. Update the <description>element:

    1
    2
    <description>This plugin demonstrates how to write integration tests for a JIRA plugin.</description>
    
  4. Save the file.

Step 3. Register the Plugin Module in the Plugin Descriptor

Now you will add the required plugin modules to your plugin descriptor at src/main/resources/atlassian-plugin.xml. The plugin descriptor is an XML file that identifies the plugin to JIRA and defines the functionality that the plugin requires.

Here's a basic plugin descriptor:

1
2
<atlassian-plugin key="${project.groupId}.${project.artifactId}" name="${project.artifactId}" plugins-version="2">
<plugin-info>
<description>${project.description}</description>
<version>${project.version}</version>
<vendor name="${project.organization.name}" url="${project.organization.url}" />
</plugin-info>
</atlassian-plugin>

Note that some of the information from the POM is transferred to the plugin descriptor using variable names such as ${project.artifactId}.

You will need the following plugin module:

1
2
<webwork1 key="showcurrentuser" name="Show Current Users Project Permissions" class="java.lang.Object">
    <actions>
        <action name="com.atlassian.plugins.tutorial.ShowUsersProjectPermissionAction"
                alias="ShowUsersProjectPermissionAction">
            <view name="success">/templates/showprojects.vm</view>
            <view name="error">/templates/showprojects.vm</view>
        </action>
    </actions>
</webwork1> 

The above code snippet introduces a webwork action into this plugin. This is not strictly necessary to write integration tests, but it adds a good example to write integration tests for.

Now you are ready to move onto writing some code to make your plugin do something.

Step 4. Write Java Classes

In this plugin, you want to write integration tests for some plugin points defined by your plugin.

To do this, you will write a webwork action that simply displays the projects in which the currently logged in user has the permission provided as a parameter. So for example, a user will be able to show all the projects that they have the Create Issue permission in. The webwork action will also perform some error checks to ensure that only logged in users can query this information and that the permission submitted as a parameter to the action is a valid JIRA permission.

First we need to construct the webwork action with a JIRA PermissionManager which will be used to check what projects a user has a given permission in:

1
2
public ShowUsersProjectPermissionAction(final PermissionManager permissionManager)
{
    this.permissionManager = permissionManager;
}

When a user submits a request to this action, we'll have to do some validation:

1
2
@Override
protected void doValidation()
{
    final User user = getRemoteUser();
    //check if a user is logged in, otherwise return with an appropriate error message
    if (user == null)
    {
        addErrorMessage("Please login to view the current user's projects for a permission.");
    }
    final int permissionType = Permissions.getType(permission);
    //then check if the permission provided as a parameter is a valid JIRA permission
    if (permissionType == -1)
    {
        addErrorMessage("Invalid permission specified.  Permission '" + permission + "' is unknown!");
    }
}

If validation passed, the doExecute() method is called to retrieve the information we're after:

1
2
@Override
protected String doExecute() throws Exception
{
    final User user = getRemoteUser();
    final int permissionType = Permissions.getType(permission);

    //retrieve all the projects that the current user has the given permission type in.
    projects = permissionManager.getProjectObjects(permissionType, user);
    return SUCCESS;
}

Finally, all webwork actions need a corresponding view:

1
2
<html>
<body>
    #if($action.hasAnyErrors())
        <ul>
            #foreach($error in $action.getErrorMessages())
                <li>$error</li>
            #end
        </ul>
    #else
        <div>The user $action.remoteUser.name has the '$action.permission' permission in the following projects:</div>
        <ul id="projects-list">
            #foreach($project in $action.projects)
            <li>$project.name ($project.key)</li>
            #end
        </ul>
    #end
</body>
</html>

Step 5. Build, Install and Run the Plugin

Follow these steps to build and install your plugin, so that you can test your code. If you have not already started the application, start it now:

  • Open a command window and go to the plugin root folder (where the pom.xml is located).
  • Run atlas-run (or atlas-debug if you might want to launch the debugger in your IDE).

From this point onwards, you can use QuickReload to reinstall your plugin behind the scenes as you work, simply by rebuilding your plugin.

FastDev and atlas-cli have been deprecated. Please use Automatic Plugin Reinstallation with QuickReload instead.

To trigger the reinstallation of your plugin:

  1. Make the changes to your plugin module.
  2. Open the Developer Toolbar.
  3. Press the FastDev icon.

    The system rebuilds and reloads your plugin:

Use live reload to view real-time updates to templates and other resources:

  1. Open the Developer Toolbar.
  2. Press the live reload icon.
    The  icon starts to revolve indicating it is on.
  3. Edit your project resources.
  4. Save your changes:
    Back in the host application, your plugin displays any user visible changes you make. 

Go back to the browser. The updated plugin has been installed into the application, and you can test your changes.

The full instructions are in the SDK guide.

You should be able to visit localhost:2990/jira/secure/ShowUsersProjectPermissionAction.jspa?permission=create to see a list of all the projects that you have the Create Issue permission in. This may require you to create some projects first.

Step 6. Write Unit and Integration Tests

There's now available a newer, object oriented way of writing integration tests. This section is for reference and legacy integration tests. We advise you to write integration tests using Page Objects.

Congratulations! You've made it to the meaty part of this tutorial. We'll only focus on integration tests since there's nothing to unit test here, really.

Before we jump into writing an integration test, here's a bit of background about how integration tests work in JIRA. JIRA's integration tests are driven by jWebUnit (albeit a fairly ancient version: 1.2). jWebUnit's purpose is essentially to simulate a browser, making HTTP requests to your web application and making assertions about the state returned in responses by your application. It provides a number of built-in methods to make this easy such as clickLink("linkid") and assertTextPresent("Sample Text").

JIRA built an integration testing framework on top of this to make certain actions in JIRA easier. Historically, integration tests would be written by extending JiraWebTest which would provide all of these utility methods to your integration test. This has now been superseded by a new integration testing framework for JIRA which provides much better documentation and is more flexible and extensible. Please have a read of the framework's API docs for more details about what's possible (make sure to checkout all the interfaces in the main package as they contain all the useful utility methods available to you).

Before we start writing an integration test, you'll have to create a localtest.properties file in your src/test/resources folder:

1
2
jira.protocol = http
jira.host = localhost
jira.port = 2990
jira.context = /jira
jira.edition = all

jira.xml.data.location = src/test/xml

JIRA's integration test framework uses this file to figure out which URL your JIRA instance is running under and where to find XML backup files your tests can import to set up the test data. At some stage in the near future, this file will be generated automatically.

Once you have your localtest.properties file in place, you can begin writing your actual integration test case in src/test/java/it:

1
2
public class ShowUsersProjectPermissionTest extends FuncTestCase

To write an integration test using JIRA's integration test framework, your test class has to extend FuncTestCase. This class provides a number of helpers such as administration, text and navigation to make integration testing easier. Note that tests not in the it directory are treated as unit tests by the atlas-integration-test command later on, which means that JIRA won't be automatically started and all the "unit tests" will fail.

Before you can start testing, we need to set up JIRA with some data that we can use to test. This is best done by importing an XML backup before a test runs:

1
2
@Before
public void setUpTest()
{
    //restore some test data.  This particular export contains 2 projects and one issue.
    administration.restoreData("TestShowUsersProjectPermission.xml");
}

This method uses the administration helper to restore the TestShowUsersProjectPermission.xml backup in JIRA.

While you can use the helper classes provided by FuncTestCase to create test data, this is generally not recommended. For example, creating a large number of issues this way can be very slow. It's better to manually create your test data first, which can then be backed up to XML and imported in your test.

Now we have JIRA setup with some data and we can start writing our first test. Let's test the webwork action we wrote earlier:

1
2
/**
 * This particular test checks that the webwork action included in this plugin provides the correct information and
 * error messages.
 */
@Test
public void testUsersProjectPermissions()
{
    navigation.logout();
    navigation.gotoPage("/secure/ShowUsersProjectPermissionAction.jspa?permission=create");
    text.assertTextPresent(new WebPageLocator(tester),
            "Please login to view the current user's projects for a permission.");
    navigation.login("admin", "admin");
    navigation.gotoPage("/secure/ShowUsersProjectPermissionAction.jspa?permission=invalidpermission");
    text.assertTextPresent(new WebPageLocator(tester),
            "Invalid permission specified.  Permission 'invalidpermission' is unknown!");
    navigation.gotoPage("/secure/ShowUsersProjectPermissionAction.jspa?permission=create");
    text.assertTextPresent(new WebPageLocator(tester), "The user admin has the 'create' permission in the following projects");
    text.assertTextSequence(new IdLocator(tester, "projects-list"), "Homosapien (HSP)", "Monkey (MKY)");
}

This particular test navigates to our webwork action by going to a specific URL and asserts that the correct text has been returned. This test does not use jWebUnit directly, but instead uses helpers provided by JIRA's integration testing framework. One particular pattern that is very useful is the text.assertTextPresent(SomeLocator) pattern. Quite often a web-page will contain the same text in several different places on a web page (for example the name of the logged in user may appear in the page header as well as in the reporter field of an issue) and it's necessary to only run an assertion in a particular part of a page. Locators are a way of limiting what part of the page an assertion is being run over. This is one of the most powerful parts of the integration testing framework and it is highly recommended to have a look at the different kinds of locators available to you.

Finally, lets demonstrate some more advanced ways one can write an integration test:

1
2
/**
 * This test does not test anything from this plugin.  It simply uses the func test framework to perform a number
 * of actions and assertions in JIRA for the purposes of this tutorial.
 * It shows how to:
 * <ul>
 * <li>Create a user</li>
 * <li>Create an issue</li>
 * <li>Add a comment to an issue</li>
 * <li>Carry out various assertions</li>
 * </ul>
 */
@Test
public void testJiraActions()
{
    /* first lets create a user */
    administration.usersAndGroups().addUser("fred", "password", "Fred Flintstone", "fred@example.com");
    /* check the user exists.  The previous action would have done this already, but lets check anyway */
    navigation.gotoAdminSection("user_browser");
    //click a link with id fred using jWebUnit
    tester.clickLink("fred");
    text.assertTextSequence(new WebPageLocator(tester),
            "Username:", "fred", "Full Name:", "Fred Flintstone", "Email:", "fred@example.com");
    //also try logging in as that user
    navigation.logout();
    navigation.login("fred", "password");
    navigation.logout();
    navigation.login("admin", "admin");
    /* Lets create a new issue */
    final String issueKey = navigation.issue().createIssue("Homosapien", "Bug", "This is a first bug");
    navigation.issue().viewIssue(issueKey);
    //various ways for asserting stuff.
    // 1. Using text assertions from JIRA's integration test framework using Locators
    //    Locators are awesome.  Very flexible ways to Locate particular parts of your webpage, and very easy to write
    //    yourself
    text.assertTextPresent(new TableLocator(tester, "issue_header_summary"), "This is a first bug");
    // 2. Using jWebUnit directly
    tester.assertTextInTable("issue_header_summary", "This is a first bug");
    // 3. Or if you're really desperate by String comparison using the HTML source returned in the response.
    final String htmlSource = tester.getDialog().getResponseText();
    assertTrue(htmlSource.contains("This is a first bug"));
    // 4. Asserting some text is not present
    text.assertTextNotPresent(new WebPageLocator(tester), "Fred Flintstone");
    /* Lets add a comment to an issue and check it was created properly */
    navigation.issue().addComment(issueKey, "I'm not sure this is the bug you're looking for.", null);
    navigation.issue().viewIssue(issueKey);
    text.assertTextPresent(new IdLocator(tester, "issue_actions_container"),
            "I'm not sure this is the bug you're looking for.");
}

This test demonstrates some more advanced things that are possible with JIRA's integration testing framework. Line 82 also demonstrates how to use jWebUnit directly via the tester variable, should this ever be necessary.

To run this test you can simply run atlas-integration-test from the command line. This will build your plugin, start up JIRA and run all tests. To run individual tests, you should also be able to start up JIRA with atlas-run and execute your test from your IDE. In Intellij, you can simply right click on a method and click on Run '<TESTNAME>':

If there's any test failures, the build will fail:

1
2
Tests run: 2, Failures: 1, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Unable to execute mojo

Embedded error: There are test failures.

Please refer to /Users/andreask/projects/atlassian/jira-integration-tests-tutorial/target/jira/tomcat6x/surefire-reports for the individual test results.

The directory mentioned in the build log will contain a text file for each test class in your integration test suite with more details about what went wrong:

1
2
-------------------------------------------------------------------------------
Test set: it.ShowUsersProjectPermissionTest
-------------------------------------------------------------------------------
Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 72.129 sec <<< FAILURE!
testUsersProjectPermissions(it.ShowUsersProjectPermissionTest)  Time elapsed: 40.25 sec  <<< FAILURE!
junit.framework.AssertionFailedError: The text 'The user admin h the 'create' permission in the following projects' could not be found via locator WebPageLocator : 1 node(s) - 'Welcome - Your Company JIRA var conte...'
        at junit.framework.Assert.fail(Assert.java:47)
        at com.atlassian.jira.functest.framework.assertions.TextAssertionsImpl.assertTextPresentImpl(TextAssertionsImpl.java:71)
        at com.atlassian.jira.functest.framework.assertions.TextAssertionsImpl.assertTextPresent(TextAssertionsImpl.java:39)
        at it.ShowUsersProjectPermissionTest.testUsersProjectPermissions(ShowUsersProjectPermissionTest.java:39)
...

In this case I misspelled the text "The user admin h the 'create' permission in the following projects" on line 39 of the ShowUsersProjectPermissionTest.

Congratulations, that's it

You should now be able to write integration tests for JIRA. Oh and don't forget to have a chocolate!

Plugin Testing Resources and Discussion

Writing integration tests using Page Objects

Smarter integration testing with TestKit

Rate this page: