SWTBot: My New friend

Blog Categories: 

Preamble: This is the first post of the new Eclipse category of BonitaSoft's community blog. As soon as we find something interesting to talk about when developing Bonita Studio, we will try to share what we learnt with our communities: BonitaSoft and Eclipse. We are also pleased to see this category aggregated to Planet Eclipse knowledge feed. We hope you'll enjoy reading these posts! The Bonita Studio developers crew

I found some time recently to take a look at SWTBot to check whether we should use it while developing Bonita Studio (part of Bonita Open Solution, based on Eclipse Gelileo 3.5.1). After a few minutes of reading wiki pages and trying it, I was convinced that SWTBot (and its GEF extension, that also works for GMF) is a must-use project for anyone who has ever it found difficult and time-consuming to write plugin tests. Only a few hours later our continuous integration build welcomed our first SWTBot based test! Here is the step-by-step of this awesome encounter:

  1. Install SWTBot on your development platform by installing SWTBot from updatesite: http://download.eclipse.org/technology/swtbot/galileo/dev-build/update-site
  2. Create a new plugin to host your test. Add these dependencies to your plugin.Dependencies for SWTBot test
  3. Write your test
    1. package org.bonitasoft.diagram.test; import org.bonitasoft.studio.model.process.diagram.part.ProcessDiagramEditor;<br />
    2. import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor;<br />
    3. import org.eclipse.swtbot.eclipse.gef.finder.SWTBotGefTestCase;<br />
    4. import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;<br />
    5. import org.eclipse.swtbot.swt.finder.widgets.SWTBotMenu;<br />
    6. import org.eclipse.ui.IEditorPart;<br />
    7. import org.junit.Assert;<br />
    8. import org.junit.Test;<br />
    9. import org.junit.runner.RunWith;<br />
    10. /** * @author Mickael Istria * */<br />
    11. RunWith(SWTBotJunit4ClassRunner.class) public class DiagramTests extends SWTBotGefTestCase { @Test public void testDiagramTest() throws Exception { SWTBotMenu menu = bot.menu("Process");<br />
    12. menu.menu("New").click(); // simulate a click on Process > New menu entry SWTBotEditor botEditor = bot.activeEditor();<br />
    13. IEditorPart editor = botEditor.getReference().getEditor(false);<br />
    14. Assert.assertTrue("New process should open a process editor", editor instanceof ProcessDiagramEditor);<br />
    15. } }
  4. Configure your test run
    1. Use JUnit4 as launcher, and don't use UIThread (SWTBot tests won't run in a UIThread)
    2. Set the product you want to test (leave default org.eclipse.platform.ide for "simple" plugins)
    3. Increase memory and set a language
  5. Run and enjoy the high code coverage that you get with so few lines of code!
  6. Ok, now let's try the GEF extension of SWTBot to check some tricky behavior in a diagram editor. This test creates a new process and then activates a tool in the design palette to create a new step, and then does some checks (and all in about a dozen lines ;):
    1. @RunWith(SWTBotJunit4ClassRunner.class) public class DiagramTests extends SWTBotGefTestCase { @Test public void testDiagramTest() throws ExecutionException { SWTBotMenu menu = bot.menu("Process");<br />
    2. menu.menu("New").click();<br />
    3. SWTBotEditor botEditor = bot.activeEditor();<br />
    4. SWTBotGefEditor gmfEditor = bot.gefEditor(botEditor.getTitle());<br />
    5. gmfEditor.activateTool("Step");<br />
    6. gmfEditor.mouseMoveLeftClick(200, 200);<br />
    7. menu.menu("Save").click();<br />
    8. IGraphicalEditPart part = (IGraphicalEditPart)gmfEditor.mainEditPart().part();<br />
    9. MainProcess model = (MainProcess)part.resolveSemanticElement();<br />
    10. Pool pool = (Pool)model.getElements().get(0); Assert.assertEquals("Pool should contain 3 nodes", 3, pool.getElements().size());<br />
    11. } }
  7. Run again, and enjoy even more: SWTBot for GEF provides a lot of very high-level Methods to manipulate your diagram. Without it, writing tests for a GMF based editor was quite difficult, and did not mimic user actions very well. With this, you can test real usage scenarios with very little code.
  8. The return on investment with SWTBot looks very good, so let's adopt it and automate test execution in a continuous integration build, leveraging the SWTBot headless framework. The following requires you to be familiar with automated PDE or RCP build and testing.
    1. Install swtbot in the platform you use to build your plugins or your RCP app. For instance, you can use the P2 director commandline application to install it from updatesite:
      1. java -jar plugins/org.eclipse.equinox.launcher_1.0.201.R35x_v20090715.jar -application org.eclipse.equinox.p2.director -artifactRepository<br />
      2. http://download.eclipse.org/technology/swtbot/galileo/dev-build/update-site -metadataRepository<br />
      3. http://download.eclipse.org/technology/swtbot/galileo/dev-build/update-site -installIU<br />
      4. org.eclipse.swtbot.eclipse.feature.group -installIU org.eclipse.swtbot.eclipse.gef.feature.group -consoleLog
    2. Install SWTBot headless test framework in your build platform: Download it from SWTBot download page, and expand it in your build platform directory.
    3. Add the SWTBot runtime and headless plugins and your new test plugin to your test feature.
      In our case, we prefered keeping only the org.eclipse.ant.optional.junit fragment and using new junit bundles to avoid conflicts between classes from org.junit and org.junit4 bundles. However, SWTBot provides some alternative fragments to support either junit3 or junit4 if you prefer.You can add the following entries in your map file for new junit bundles:
      1. ** Use newer JUnit as described in http://wiki.eclipse.org/Eclipse/Testing/JUnit4_Changes !<br />
      2. ** Should facilitate integration with SWTBot headless plugin@org.junit,4.8.1=GET,http://download.eclipse.org/tools/orbit/downloads/drops/S20100120144102/bundles/org.junit_4.8.1.v4_8_1_v20100114-1600.zip,unpack=true<br />
      3. plugin@org.junit4=v20100104,:pserver:anonymous:@dev.eclipse.org:/cvsroot/eclipse,,org.junit4<br />
      4. plugin@org.eclipse.jdt.junit.runtime=v20091201-0800,:pserver:anonymous:@dev.eclipse.org:/cvsroot/eclipse,,org.eclipse.jdt.junit.runtime
    4. In the piece of script that runs your test, add the following test invocation command:
      1. <echo>SWTBot test</echo><br />
      2. <java dir="${eclipse.test.home}" fork="true" output="${eclipse.test.home}/output.txt" logError="true" classname="org.eclipse.core.launcher.Main" failonerror="false"><br />
      3. <classpath><br />
      4. <fileset dir="${eclipse.test.home}/plugins"><br />
      5. <include name="org.eclipse.equinox.launcher_*.jar"/><br />
      6. </fileset><br />
      7. </classpath><br />
      8. <arg line="-application org.eclipse.swtbot.eclipse.junit4.headless.swtbottestapplication"/><br />
      9. <arg line="-testPluginName org.bonitasoft.studio.diagram.test"/><br />
      10. <arg line="-testApplication org.bonitasoft.studio.application.application"/> <arg line="-className org.bonitasoft.studio.diagram.test.DiagramTests"/><br />
      11. <arg line="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,junit-results.xml"/><br />
      12. <arg line="-nl fr"/><br />
      13. <arg line="-consoleLog"/><br />
      14. <jvmarg line="-Xms40m -Xmx348m -XX:MaxPermSize=256m -XX:+HeapDumpOnOutOfMemoryError"/><br />
      15. </java>
  9. At this point, when everything is working, you should be one of the happiest people in the world: You have not only reduced the difficulty and cost of writing tests, but also increased the coverage and the realism of your tests

Congrats and thanks to SWTBot developers for making this possible! We love playing with it when developing Bonita Open Solution! If someone has a better solution for this integration, please tell me!