Revision History | |||
---|---|---|---|
18 April 2014 | Modified the SWTBot test so that it can be reused also in a test suite (see the comments to this post). |
I happened to give a lecture at the University of Florence on Test Driven Development; besides the standard Junit tests I wanted to show the students also some functional tests with SWTBot. However, I did not want to introduce Eclipse views or dialogs, I just wanted to test a plain SWT application with SWTBot.
In the beginning, it took me some time to understand how to do that (I had always used SWTBot in the context of an Eclipse application); thanks to Mickael Istria, who assisted me via Skype, it ended up being rather easy.
You can find this example here: https://github.com/LorenzoBettini/junit-swtbot-example.
The SWT application is a simple dialog that computes the factorial of the given input (nothing fancy, its code can be seen here).
If we now want to test this SWT application with SWTBot, we can write an abstract base class that we use for our tests (see also the online code)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
package mathutils.ui.tests; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import mathutils.ui.MathUtilsWindow; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swtbot.swt.finder.SWTBot; import org.eclipse.swtbot.swt.finder.SWTBotTestCase; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; public abstract class AbstractMathUtilsWindowTest extends SWTBotTestCase { protected SWTBot bot; protected static Thread uiThread; protected static Shell shell; private final static CyclicBarrier swtBarrier = new CyclicBarrier(2); @BeforeClass public static void setupApp() { if (uiThread == null) { uiThread = new Thread(new Runnable() { @Override public void run() { try { while (true) { // open and layout the shell MathUtilsWindow window = new MathUtilsWindow(); window.open(); shell = window.getShell(); // wait for the test setup swtBarrier.await(); // run the event loop window.eventLoop(Display.getDefault()); } } catch (Exception e) { e.printStackTrace(); } } }); uiThread.setDaemon(true); uiThread.start(); } } @Before public final void setupSWTBot() throws InterruptedException, BrokenBarrierException { // synchronize with the thread opening the shell swtBarrier.await(); bot = new SWTBot(shell); } @After public void closeShell() throws InterruptedException { // close the shell Display.getDefault().syncExec(new Runnable() { public void run() { shell.close(); } }); } protected void assertResultGivenInput(String input, String expectedResult) { bot.textWithLabel("input").setText(input); bot.button("Compute").click(); assertEquals(expectedResult, bot.textWithLabel("result").getText()); } } |
And we use this base class in our tests, for instance
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package mathutils.ui.tests; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.BlockJUnit4ClassRunner; @RunWith(BlockJUnit4ClassRunner.class) public class MathUtilsNonValidInputWindowTest extends AbstractMathUtilsWindowTest { @Test public void testEmptyInput() { assertResultGivenInput(" ", "Empty input"); } @Test public void testNonValidIntegerInput() { assertResultGivenInput("foo", "Not a valid input"); } @Test public void testNonValidInput() { assertResultGivenInput("-1", "Not a valid input"); } } |
There are a few things to note in the abstract base class:
- You need to spawn the application in a new thread (the bot will run in a different thread)
- You must start the application before creating the bot (otherwise the Display will be null)
- after that you can simply use SWTBot API as you’re used to.
Note that the thread will create our window and then it will enter the event loop; this thread synchronizes with the @Before method (executed before each test), which creates the SWTBot (using the shell created by the thread). The @After method (executed after each test), will close our window, so that each test is independent from each other. The thread executes in an infinite loop, thus as soon as the shell is closed it will create a new one, etc.
Of course, this must be executed as a “Junit test”, NOT as a “Plug-in Junit test”, neither as a “SWTBot Test”, since we do not want any Eclipse application while running the test:
In the sources of the example you can find also the files to run the tests headlessly with Buckminster or with Maven/Tycho. Just enter the directory mathutils.build and
1 |
ant -f build.ant |
for Buckminster or
1 |
mvn clean verify |
for Maven.
For Buckminster, you just need to save the launch configuration you used to run the test, and use the junit command:
1 |
junit -l "mathutils.ui.tests/mathutils.ui.tests.launch" --flatXML -o "${buckminster.output.root}/test.results/mathutils.ui.tests.launch.result.xml" |
For Tycho, you must specify <packaging>eclipse-test-plugin</packaging>, but without further configuration. This will default useUIHarness to false.
During the headless run, first the Junit tests for the implementation of the factorial will be executed (these are not interesting in the context of SWTBot) and then the SWTBot tests will be executed.