Wednesday 18 December 2013

Executing Eclipse Plugin JUnit tests in real-time without needing to restart Eclipse (with no mocking)

One of the key capabilities that I wanted to have after Programming Eclipse in Real-Time (using an 'Groovy based' Eclipse Plug-in), was to be able to run JUnit tests (including tests using STWBot)  in the live (under debug) Eclipse instance (called test Eclipse below).

This would allow me to code in a very quick/efficient TDD workflow, since I wouldn't have to wait 15s to 30s to see execution results for new JUnit tests or major/minor changes to existing JUnit tests.

The good news is that by using the GroovyExecution API that I wrote for the TeamMentor Eclipse Plugin, I was able to dynamically load and run the class files of the JUnit tests to execute, which was already a massive milestone, since that gave me 80% of what I needed. But it was only after Adding and using new API methods, that are consumed by an Eclipse Plugin under development (without Eclipse restart) and having JRebel enabled, that I had the full dynamic environment (where changes to the main plugin code and changes to JUnit test code did NOT require an Eclipse restart).

Here is a walkthrough of how it works (still a bit rough around the edges , but already a really powerful workflow).

As an example lets look at the tm.eclipse.helpers.images class that I added yesterday to the Plugin Apis (when adding the Groovy Script to create a view that shows the images available in the ISharedImages class).

The objective of this class it to make it really easy to get one of the images that is provided via the ISharedImages class

And what we want is to write a JUnit test to check that it is working as expected.

So in the TeamMentor.Eclipse.UxTests project, I added a new images_Test class,

 With a simple test method (which will trigged a fail exception with the message 'Not implemented')

Next, I switched to the test Eclipse (the one opened from the dev Eclipse), and wrote this Groovy script:

...which when executed returns this:

... which is the JUnit execution result of the tm.eclipse.helpers.Images_Test JUnit test class (note how the names JUnit test method failed with the Not Implemented exception)

Since in that script there are already a number of helper methods in action, it might be easier to understand what is going on if I de-factored it to this (code in this gist):

Basically what is happening is that I'm invoking the JUnitCore.runClasses static method from this Groovy execution environment (which is running under the current eclipse).

And since my objective is always to make code/scripting as simple as possible, I added the static method dev_Execute_TM_JUnit to the GroovyExecution API, so that we can execute UxTests JUnit tests like this:

Now that we have the ability to execute JUnit tests in the current Eclipse in real-time (did I mention that I don't have to restart Eclipse in between executions :)  ), its time to write the the Images_Test test.

Depending if I'm working with two monitors (or not), I usually open the two main files (in this case and in the  dev Eclipse, in a dev environment that looks like this:

In the image above:

  • Top left is the file (which is the one we are writing the JUnit test about)
  • Botom left is the (which is the JUnit test file)
  • Right is the Groovy Execution environment with only the  return GroovyExecution.dev_Execute_TM_JUnit("tm.eclipse.helpers.Images_Test"); script (which will execute the JUnit test in less than 1 sec :)
    • Note how in this execution result window/text, the runCount value is 2 and the the names method fail message is Hello JUnit World (which match the small changes I made to the file before I took that screenshot)
The next step is to actually write the JUnit test ...

... which after a number of executions (all done without restarting Eclipse)

... looked like this:

... with the execution result looking like this:

That JUnit test is a good example of one of the ways I like to do TDD, since I'm also using those tests to check my expectation and understanding of a particular Eclipse API.