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.
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:
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.
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
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:
com.atlassian.plugins.tutorial
jira-integration-tests-tutorial
1.0
com.atlassian.plugins.tutorial
Now you need to edit your POM (Project Object Model definition file) to add some metadata about your plugin and your company or organisation.
Edit the pom.xml
file in the root folder of your plugin.
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>
Update the <description>
element:
1 2<description>This plugin demonstrates how to write integration tests for a JIRA plugin.</description>
Save the file.
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.
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 2public 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>
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:
pom.xml
is located).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:
Use live reload to view real-time updates to templates and other resources:
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.
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 2jira.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 2public 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 2Tests 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
Rate this page: