|
Atlassian plugins depend on Maven 2 for running their test suites. This is advantageous because all plugins are always tested the same way, and because Bamboo, our continuous integration server, can do likewise. Building a suite of tests for your plugin is extremely important. We're big believers in Test Driven Development, and we think that writing tests as you go is one of the best ways to ensure you build a high-quality and functional application. Additionally, having a comprehensive suite of tests means that other developers in the community (or even Atlassian developers) can jump in and collaborate on your plugin without fear of breaking anything inadvertently. Our products are always moving forward. We enhance and change things. We move or deprecate APIs. A plugin that works perfectly in Confluence 2.5 might change in Confluence 2.6, in subtle or profound ways. That's the primary reason that we run the Developer Bamboo Server – to alert us to any changes that might break plugins. When those breakages occur, we might be able to go back and do things another way that preserves compatibility. Or, at the very least, we can notify developers in advance of a new release about what has changed. Continuous Integration is a crucial safeguard, and it depends largely on a comprehensive test suite to alert us when things break. Unit TestsThe first tests you should write for your plugin are unit tests. Unit tests are designed to exercise individual chunks of your code in isolation. To run unit tests, the default Maven 'test' command works: > mvn clean test For examples of unit tests, take a look at the Confluence Chart Plugin test or the JIRA Fisheye Plugin tests. Integration, or Functional, TestsStart by reading this great testing tutorial from Customware. Integration Testing is designed to test your code inside the the Atlassian product. The goal is confirm that the plugin functionality works as designed, and is compatible with various versions of the product. To achieve this goal, Maven will actually deploy your plugin to a running version of JIRA or Confluence and then run tests that you write to exercise its functionality. To run integration tests, the command is: > mvn clean integration-test When you run this command, Maven will start up a new instance of JIRA or Confluence, running inside Tomcat, and install some default data into it: things like a license, database configuration, and an admin account. This will save you the effort of having to set up the product each time. It's important to note that the license installed is a unique one: it is perpetually valid, but only for a period of three hours (which should be more than enough time to run your tests.) Should you keep going longer than three hours, the license system will lock you out, and you'll need to restart it to proceed. For examples of integration tests, take a look at the JIRA Fisheye Plugin tests. A Confluence example is forthcoming. Skipping testsTo skip the various test phases, add one of the following options to your Maven command: > -Dmaven.test.skip=true - skips both unit and integration tests > -Dmaven.test.unit.skip=true - skips unit tests > -Dmaven.test.it.skip=true - skips integration tests NotesOftentimes, your functional tests will need to set up the product in a particular state. You may wish to do this by creating an XML export of JIRA or Confluence data in a known state, and importing that data at the beginning of your tests. XML data that is usually imported by the tests into the running product should be placed in src/test/xml/ directory provided. Packages containing integration tests should start with it. For instance:
This is due to inability of Maven to discriminate unit tests from integration tests. This will be resolved in the coming Maven 2.1 and we will take care of the migration for you then. System PropertiesThe following system properties are available to control the behaviour of the integration test harness:
Debugging your failing testsAll of the necessary output generated by tests will be found under target subdirectory of your module. The following files are useful places to look at dire times:
1 — The <product> directory will be found under debug if the Maven profile, plugin-debug is activated. That means, when the plugin-debug profile is activated, the directory becomes debug/<product>/output.log. This isolates debug resources and the actual integration test resources. |
Except where otherwise noted, content in this space is licensed under a Creative Commons Attribution 2.5 Australia License.
Comments (17)
Apr 01, 2008
Wim Deblauwe says:
I am trying to write an integration test for my JIRA plugin, but things are not ...I am trying to write an integration test for my JIRA plugin, but things are not working out very well. I first manually setup a JIRA instance with 1 project and 1 issue in that project (running under mvn -Pplugin-debug).
I then exported the data to xml and wrote a unit test like this:
However, this gives the following error:
My default Locale is not English, so I tried to specify it before the setUp() happens, but it does not seem to make a difference. I get the same error.
Apr 08, 2008
Roman Bosak says:
Hi, I faced same problem yesterday. I fixed it simply adding <jvmargs>-...Hi,
I faced same problem yesterday. I fixed it simply adding
tag between my build properties.
Apr 11, 2008
Wim Deblauwe says:
Where exactly do you specify this in the pom.xml?Where exactly do you specify this in the pom.xml?
May 12, 2008
Roman Bosak says:
Within project/properties element.Within project/properties element.
Apr 08, 2008
Roman Bosak says:
Hi, I have also problem with integration tests. They gives me following error in...Hi,
I have also problem with integration tests. They gives me following error into output.log file:
2008-04-07 21:42:10,812 main ERROR [web.action.util.JiraLicenseUtils] The current license is too old (Tue Aug 22 07:00:00 CEST 2006) to run this version (3.12 - Wed Nov 28 00:00:00 CET 2007) of JIRA.Do yo know what is wrong? I expected that the licence will be installed by integration test framework automatically. But looks like it is not
Can anybody help me?
May 12, 2008
Roman Bosak says:
I finally found the solution by myself Even if I am testing plugin for JIRA 3.1...I finally found the solution by myself
Even if I am testing plugin for JIRA 3.11 I should usine JIRA 3.12 test-lib version. You can set it up using property jira.test-lib.version as defined above.
The problem is in JIRA 3.11 test-lib which really uses old licence.
Jul 08, 2008
Gert-Jan Bartelds says:
Hello, I followed the instructions listed at How to build an Atlassian Plugin a...Hello,
I followed the instructions listed at How to build an Atlassian Plugin and was able to produce a skeleton plugin jar for a plugin named 'first'.
Next, I proceeded to run mvn clean integration-test, which fails with a timeout at the cargo start-container task. The log file (target\output.log) reveals:
Jul 8, 2008 5:18:19 PM org.apache.tomcat.util.digester.Digester fatalError
SEVERE: Parse Fatal Error at line 33 column 35: An invalid XML character (Unicode: 0xc) was found in the value of attribute "url" and element is "Resource".
org.xml.sax.SAXParseException: An invalid XML character (Unicode: 0xc) was found in the value of attribute "url" and element is "Resource".
I looked at that line in conf/server.xml , and it shows like this :
url='jdbc:hsqldb
:jirapluginsirst arget/jira-home/database'
Obviously the variable part of the constructed url is mangled and contains 0x0c and 0x09 characters.
Has anybody seen this before? Does anybody have a solution?
Thanks in advance, Gert-Jan Bartelds
Jul 08, 2008
Gert-Jan Bartelds says:
I found a workaround to the mangled url problem: in file MAVEN_REPO\com\atlassi...I found a workaround to the mangled url problem:
in file MAVEN_REPO\com\atlassian\jira\plugins\jira-plugin-base\8\jira-plugin-base-8.pom , line 227, it says:
cargo.datasource.url=jdbc:hsqldb:${atlassian.product.config.directory}/jira-home/database|
Running mvn --debug showed that ${atlassian.product.config.directory} is correctly expanded, but uses backward slashes instead of forward (I'm on Windows...). Replacing the line with
cargo.datasource.url=jdbc:hsqldb:d:\jira\plugins\first\target/jira-home/database|
made no difference. Changing to forward slashes
cargo.datasource.url=jdbc:hsqldb:d:/jira/plugins/first/target/jira-home/database|
finally did the trick
For now I can live with this workaround, but I would welcome a more permanent solution.
Gert-Jan
Aug 22, 2008
Jeff Kirby says:
Hi, I'm new to developing JIRA plug-ins.It looks like com.atlassian.jira.webtest...Hi, I'm new to developing JIRA plug-ins.It looks like com.atlassian.jira.webtests.JIRAWebTest is a good resource for writing unit tests. Is there documentation, like the javadoc API, on this class somewhere?
Thanks,
Jeff
Nov 25, 2008
Ugo Cei says:
When I try to run some integration tests I wrote for my plugin, I always get the...When I try to run some integration tests I wrote for my plugin, I always get the following error:
I get the same errors when trying to run integration tests for the JIRA Fisheye Plugin.
How do I set up a database connection for use by the tests? I would use an embedded HSQLDB instance, if this is possible.
Jan 20
Gus Gould says:
Along the same lines, but one step further and a bit messier... I've used XBean...Along the same lines, but one step further and a bit messier...
I've used XBeans (http://geronimo.apache.org/xbean/) and Spring to inject a JNDI datasource for unit testing, but I can't work out how to get some sort of JTA working within the the tests.
At the moment I've dropped the following into my tests:
@BeforeClass
public static void setUpClass() throws Exception
{
try
{
// Create initial context
System.setProperty( Context.INITIAL_CONTEXT_FACTORY,
"org.apache.xbean.spring.jndi.SpringInitialContextFactory" );
//-- Loads initial context - and data found in jndi.xml
new InitialContext();
} catch( NamingException ex ) {
ex.printStackTrace();
}
}
and the following jndi.xml defining a HSQLDB connection:
<beans xmlns="http://www.springframework.org/schema/beans"
="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="jndi" class="org.apache.xbean.spring.jndi.DefaultContext">
<property name="entries">
<map>
<entry key="java:comp/env/jdbc/JiraDS">
<bean id="jiraDS" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
destroy-method="close" singleton="false" >
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:testspace" />
<property name="username" value="sa" />
<property name="password" value="" />
<property name="connectionProperties">
<map>
<entry key="minEvictableIdleTimeMillis" value="4000" />
<entry key="timeBetweenEvictionRunsMillis" value="5000" />
<entry key="dialect" value="org.hibernate.dialect.OracleDialect"/>
<entry key="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
</map>
</property>
</bean>
</entry>
</map>
</property>
</bean>
</beans>
That all seems to hook up and work fine, until something unedr the hood tries to get a UserTransaction and it all falls to bits:
2009-01-21 16:11:06,299 main WARN [core.entity.transaction.JNDIFactory] NamingException while finding UserTransaction named java:comp/env/UserTransaction in JNDI.
javax.naming.NamingException: scheme java not recognized
at org.apache.xbean.spring.jndi.DefaultContext.lookup(DefaultContext.java:131)
at javax.naming.InitialContext.lookup(Unknown Source)
at org.ofbiz.core.entity.transaction.JNDIFactory.getUserTransaction(JNDIFactory.java:109)
at org.ofbiz.core.entity.TransactionFactory.getUserTransaction(TransactionFactory.java:91)
at org.ofbiz.core.entity.TransactionUtil.getStatus(TransactionUtil.java:80)
at org.ofbiz.core.entity.jdbc.SQLProcessor.getConnection(SQLProcessor.java:279)
at org.ofbiz.core.entity.jdbc.SQLProcessor.prepareStatement(SQLProcessor.java:318)
at org.ofbiz.core.entity.GenericDAO.selectListIteratorByCondition(GenericDAO.java:1024)
at org.ofbiz.core.entity.GenericDAO.selectByAnd(GenericDAO.java:595)
at org.ofbiz.core.entity.GenericHelperDAO.findByAnd(GenericHelperDAO.java:134)
at org.ofbiz.core.entity.GenericDelegator.findByAnd(GenericDelegator.java:792)
at org.ofbiz.core.entity.GenericDelegator.findByAnd(GenericDelegator.java:777)
at org.ofbiz.core.entity.GenericDelegator.findByAnd(GenericDelegator.java:754)
at com.opensymphony.module.propertyset.ofbiz.OFBizPropertySet.getKeys(OFBizPropertySet.java:87)
at com.opensymphony.module.propertyset.AbstractPropertySet.getKeys(AbstractPropertySet.java:292)
at com.opensymphony.module.propertyset.PropertySetCloner.cloneProperties(PropertySetCloner.java:114)
at com.opensymphony.module.propertyset.PropertySetManager.clone(PropertySetManager.java:61)
at com.atlassian.jira.propertyset.JiraCachingPropertySet.init(JiraCachingPropertySet.java:664)
at com.opensymphony.module.propertyset.PropertySetManager.getInstance(PropertySetManager.java:45)
at com.opensymphony.module.propertyset.PropertySetManager.getInstance(PropertySetManager.java:22)
at com.atlassian.jira.config.properties.PropertiesManager.loadPropertySet(PropertiesManager.java:82)
at com.atlassian.jira.config.properties.PropertiesManager.<init>(PropertiesManager.java:28)
at com.atlassian.jira.config.properties.PropertiesManager.getInstance(PropertiesManager.java:38)
at com.atlassian.jira.config.properties.ApplicationPropertiesImpl.getPropertiesManager(ApplicationPropertiesImpl.java:48)
at com.atlassian.jira.config.properties.ApplicationPropertiesImpl.getString(ApplicationPropertiesImpl.java:96)
at com.atlassian.jira.config.properties.ApplicationPropertiesImpl.getDefaultBackedString(ApplicationPropertiesImpl.java:122)
at com.atlassian.jira.ComponentManager.registerExtensions(ComponentManager.java:487)
at com.atlassian.jira.ComponentManager.initialise(ComponentManager.java:366)
at com.atlassian.jira.ComponentManager.<init>(ComponentManager.java:352)
at com.atlassian.jira.ComponentManager.<clinit>(ComponentManager.java:336)
at com.atlassian.jira.issue.search.SearchRequest$1.getIssueSearcherManager(SearchRequest.java:113)
at com.atlassian.jira.issue.search.SearchRequest.<init>(SearchRequest.java:142)
at com.atlassian.jira.issue.search.SearchRequest.<init>(SearchRequest.java:149)
at com.atlassian.jira.issue.search.SearchRequest.<init>(SearchRequest.java:172)
at com.atlassian.jira.issue.search.SearchRequest.<init>(SearchRequest.java:159)
...
Any ideas how to get some sort of TransactionManager working for this connection? I have several fugly search methods that can't easily/safely be mocked out for testing a chunky plugin that I am trying to make a whole lot less chunky (incuding pulling out hardcoded DB dependencies). Any help would be much appreciated.
Mar 20
Sergiusz Michalski says:
Hi, The main problem is that ofbiz JNDIFactory is doing a direct type casting t...Hi,
The main problem is that ofbiz JNDIFactory is doing a direct type casting to javax.transaction.UserTransaction and/or javax.transaction.TransactionManager
In order to workaround it you need to use transaction manager that can run standalone outside container i.e. JOTM as well as wrap it into your class that implements both interfaces: UserTransaction and TransactionManager
i.e.
<code>
package com.unit.jira.plugin.automatedtransition;
import javax.naming.NamingException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import org.objectweb.jotm.Jotm;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.transaction.jta.JotmFactoryBean;
public class UnitTransactionManager implements TransactionManager, UserTransaction {
private Jotm jotm;
public UnitTransactionManager() {
try
catch (NamingException e)
}
private TransactionManager getTransactionManager()
/// TransactionManager interface ///
public void begin() throws NotSupportedException, SystemException
public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException
public void rollback() throws IllegalStateException, SecurityException, SystemException
public void setRollbackOnly() throws IllegalStateException, SystemException
public int getStatus() throws SystemException
public Transaction getTransaction() throws SystemException
public void setTransactionTimeout(int seconds) throws SystemException
public Transaction suspend() throws SystemException
public void resume(Transaction tobj) throws InvalidTransactionException, IllegalStateException, SystemException
}
</code>
And finally the spring context
<code>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close" scope="prototype">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:." />
<property name="username" value="sa" />
<property name="password" value="" />
<property name="connectionProperties">
<map>
<entry key="minEvictableIdleTimeMillis" value="4000" />
<entry key="timeBetweenEvictionRunsMillis" value="5000" />
</map>
</property>
</bean>
<bean id="transactionManager" class="com.unit.jira.plugin.automatedtransition.UnitTransactionManager" />
<bean id="jndi" class="org.apache.xbean.spring.jndi.DefaultContext">
<property name="entries">
<map>
<entry key="java:comp/env/jdbc/JiraDS" value-ref="dataSource" />
<entry key="java:comp/env/UserTransaction" value-ref="transactionManager" />
</map>
</property>
</bean>
</beans>
</code>
And tandan tadan !!!!- transational context works and I was able to import the jira database xml file into my junit test (not integration-test) !!! and test core plugin functionality from junit !!!
Quick code that is doing import:
<code>
System.setProperty(Context.PROVIDER_URL, "com/mforma/jira/plugin/automatedtransition/jndis.xml");
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.xbean.spring.jndi.SpringInitialContextFactory");
InitialContext ic = new InitialContext();
// piece above can be placed of course somewhere in setUp() function and polished in order not to use File references but classpath etc... of unit test but this is just for showing idea so... forgive me guys
File file = new File("/home/me/path_to_the_jira_xml_data_dump/database-values-plugin-it-data.xml");
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
JiraXmlConfigBean config = new JiraXmlConfigBean();
JiraXmlImportDataBean importDataBean = new JiraXmlImportDataBean(bufferedReader, config);
DefaultJiraDataImporter jiraDataImporter = new DefaultJiraDataImporter(importDataBean);
jiraDataImporter.doImport();
</code>
Jan 21
Roberto Dominguez says:
Tried testing against Confluence 2.10.1 but looks like the license provided in ...Tried testing against Confluence 2.10.1 but looks like the license provided in atlassian.product.test-lib.version=1.4.2 has expired:
is there a way of overriding the license file? (other than rebuilding the test-lib). I tried bundling my own license.txt in the test classes but wasn't loaded.
Jan 29
Roberto Dominguez says:
Ok, figured out myself. For testing on 2.10, I had to define the right data set:...Ok, figured out myself. For testing on 2.10, I had to define the right data set:
Jan 30
Roberto Dominguez says:
Here's a post on my experience getting integration testing going on Confluence: ...Here's a post on my experience getting integration testing going on Confluence:
http://www.mundoreves.com/display/HOME/2009/01/29/Confluence+plugin+integration+testing
Apr 07
Sinan Karasulu says:
Hi. I'm new to Creating confluence plugins. Can I ask How I can use my ec...Hi.
I'm new to Creating confluence plugins. Can I ask How I can use my eclipse debugger to step threw the plugin code to see what is happening as the server is running.
using confluence 2.8.2
on windows xp platform.
Apr 07
Sergiusz Michalski says:
Hi Sinan, The main step you need to do is to run your Tomcat server on which th...Hi Sinan,
The main step you need to do is to run your Tomcat server on which the Jira is based in the debug mode.
In order to do it, follow the steps (example is for Linux users - standalone server):
1. Under $JIRA_DIR/bin directory locate file called startup.sh
locate the line with sth like:
exec "$PRGDIR"/"$EXECUTABLE" start "$@"
and replace it with i.e.:
export JPDA_ADDRESS=23989;
exec "$PRGDIR"/"$EXECUTABLE" jpda start "$@"
I'd like to point out that JPDA_ADDRESS is reflecting on your debug port
2. Just simply start the jira using startup script mentioned above
3. In Eclipse debug configuration choose "Attach mode" for remote debugging and select host (127.0.0.1 by default) + port: JPDA_ADDRESS (23989 in our example) then click "Attach / OK" - that's all now you can set the entry point under your eclipse debug view in the interested code places you want to investigate.
For jira war distribution the approach is equivalent - you need only to start Tomcat in debug mode - nothing more is required.
Cheers,
Sergiusz