Monday, July 21, 2014

Getting Started with the new SoapUI Plugin system

SoapUI 5.1 introduces a totally revamped plugin-architecture that makes it substantially easier to extend SoapUI with custom actions, teststeps, assertions, etc. The main parts of this are:

  • An improved PluginLoader that loads plugins into their own ClassLoader - allowing plugins to have 3rd party dependencies that don't collide with other plugins or SoapUI itself
  • An annotation-based extension mechanism for creating plugin classes (no more XML...)
  • An integrated PluginManager that allows you to install/uninstall/upgrade plugins via the UI
  • A plugin-repository hosted by SmartBear to which you can submit your own plugins so anyone can download them via the PluginManager
  • A maven archetype for easily creating new plugin projects using either java or groovy as the development language

Let's create a simple plugin to illustrate some of these steps.

Prerequisites


As you hopefully know - SoapUI is a desktop tool for all aspects of API Testing that you'll need to download and install from smartbear.com. Creating plugins is simplified by having a java/groovy development environment, for example Intellij or eclipse. You'll also need maven for the actual building of the plugins. I won't dive any more into this since I expect you to have all this sorted out already :-)

Creating a simple search plugin


The maven archetype will generate the basic plugin project for you; open a command-prompt in a folder where you want to create the plugin project and type the following:

mvn archetype:generate -DarchetypeGroupId=com.smartbear.maven.archetypes -DarchetypeArtifactId=soapui-plugin-archetype -Dmaven.repo.remote=http://www.eviware.com/repository/maven2

This will download and install the maven archetype and start generation of the plugin project. You will be prompted for a number of properties - set these as follows:
  • groupId: "com.mycompany"
  • artifactId : "my-soapui-plugin"
  • version : "1.0.0"
  • package : <empty>
  • language : "java"
  • type : "Action"
maven will do some work and generate the default project for you - you should see something in the line of the following in your command-prompt:




Now go into the created "my-soapui-plugin" directory and run "mvn install" from the command-line - this will build the plugin and package it for installation - you will get something like the following:




Let's install this plugin in SoapUI 5.1 - start SoapUI and open the PluginManager from the main toolbar:




Select the "Load plugin from file..." button at the bottom of the PluginManager dialog and navigate to the generated plugin in the target folder of the plugin project:




Press "Open" and the plugin should get installed:




Close the Plugin Manager and right-click on any open project in your SoapUI workspace (this is where the default plugin action is installed) - you'll see the plugin action at the bottom of the menu:




Presto - that was easy! Your first SoapUI Plugin is installed and running - and you didn't write one line of code!

Customizing the plugin to do a search in our project


Although it works - the plugin currently doesn't do much (it just shows a hello message). Let's improve the plugin to actually do something useful - search and open a list of matching items in the selected project.

Open the project in your java development environment (i'll leave the details of that to you...) and navigate to the PluginAction.java file that was generated via the maven archetype:




You can see the default implementation of the perform method - it just shows a "Hello from..." message in a popup window.

Now comes the hard part - knowing your way around the SoapUI object model and APIs - have a look at The SoapUI Object Model to get an introduction and pointers to relevant javadocs.

What we'll do is change the perform method to first prompt for a search string, then do a recursive search through all items in the select project to find those with a matching name (using the specified search string as a regular-expression) - and finally display the results in a ModelItemListDesktopPanel:




As you can see, I've also brushed up some of the metadata in the constructor - and the generated PluginConfig.java file has been updated accordingly:




together with updated metadata in the pom;

    <name>SoapUI Search Plugin</name>
    <artifactId>soapui-search-plugin</artifactId>
    <groupId>com.smartbear.soapui.plugins</groupId>

Now when we build and install the plugin (uninstall the previous version first in the Plugin Manager) - we get the following item in the popup menu:




which prompts as follows when selected:




and uses the specified regular-expression to find items in the selected project and display them:




Double-clicking an item in this window opens the corresponding desktop panel for editing. Marvellous!

Next Steps


That's all there was to it - the plugin is ready to by submitted to the plugin repository and spread to thousands of SoapUI users out there.

The above plugin is available on GitHub - feel free to fork and improve it, ask questions or just use it. 

And as always - I'm looking forward to hearing from you.

/Ole

22 comments:

  1. Thanks for the excellent demonstration, Ole..!!
    I am a frequent visitor of your blog. I would like to receive a favor from you, demanding a post on initial setup on SoapUI Plugin development environment using Eclipse and Maven. This seems to be erroneous when I try to start with plugin development. Could you please help in this regard.

    Thanks,
    Samy

    ReplyDelete
  2. Hi Samy,

    thanks for this - I'll try to write something up in this regard - please also consider signing up to the SoapUI plugin-development webinar (http://olensmar.blogspot.se/2014/07/getting-started-with-new-soapui-plugin.html) to learn more!

    kind regards,

    /Ole

    ReplyDelete
    Replies
    1. Ole, Thanks for the consideration...!!! I have already registered for the suggested webinar. Awaiting to see you on that!

      Delete
  3. Doesn't work actually. Can't create project from archetype. Probably we have to use -DarchetypeCatalog=http://www.eviware.com/repository/maven2 instead of -Dmaven.repo.remote=http://www.eviware.com/repository/maven2 ?

    ReplyDelete
  4. And doesn't work for "TestStep" type:
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD FAILURE
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 2.345 s
    [INFO] Finished at: 2014-10-02T11:14:09+02:00
    [INFO] Final Memory: 12M/219M
    [INFO] ------------------------------------------------------------------------
    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.
    1:compile (default-compile) on project averagetime-soapui-plugin: Compilation fa
    ilure: Compilation failure:
    [ERROR] C:\!WORK\SOA\SOAPUI\AverageReponseTime\averagetime-soapui-plugin\src\mai
    n\java\com\andhr\soapui\PluginTestStep.java:[11,-1] 1. ERROR in C:\!WORK\SOA\SOA
    PUI\AverageReponseTime\averagetime-soapui-plugin\src\main\java\com\andhr\soapui\
    PluginTestStep.java (at line 11)
    [ERROR] import com.eviware.soapui.plugins.auto.PluginTestStep;
    [ERROR] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    [ERROR] The import com.eviware.soapui.plugins.auto.PluginTestStep conflicts with
    a type defined in the same file
    [ERROR] ----------
    [ERROR]
    [ERROR] Found 2 errors and 0 warnings.

    Any ideas?

    ReplyDelete
  5. Thanks for this - I'm really sorry for your troubles. You are correct, please use

    -DarchetypeCatalog=http://www.eviware.com/repository/maven2

    instead of -Dmaven.repo.remote

    I've uploaded an update to the plugin that should fix your issue with the TestStep type... please let me know if you're still having troubles. Thanks!

    /Ole

    ReplyDelete
    Replies
    1. Hi. New error :) :
      Confirm properties configuration:
      groupId: com.soapui.andhr
      artifactId: average-test-step
      version: 1.0.0
      package: com.soapui.andhr
      language: Java
      type: TestStep
      Y: : y
      [INFO] -------------------------------------------------------------------------
      ---
      [INFO] Using following parameters for creating project from Archetype: soapui-pl
      ugin-archetype:1.0
      [INFO] -------------------------------------------------------------------------
      ---
      [INFO] Parameter: groupId, Value: com.soapui.andhr
      [INFO] Parameter: artifactId, Value: average-test-step
      [INFO] Parameter: version, Value: 1.0.0
      [INFO] Parameter: package, Value: com.soapui.andhr
      [INFO] Parameter: packageInPathFormat, Value: com/soapui/andhr
      [INFO] Parameter: package, Value: com.soapui.andhr
      [INFO] Parameter: version, Value: 1.0.0
      [INFO] Parameter: groupId, Value: com.soapui.andhr
      [INFO] Parameter: type, Value: TestStep
      [INFO] Parameter: language, Value: Java
      [INFO] Parameter: artifactId, Value: average-test-step
      [ERROR] ResourceManager : unable to find resource 'Java/TestStep.inc' in any res
      ource loader.
      [ERROR] #parse(): cannot find template 'Java/TestStep.inc', called from template
      archetype-resources/src/main/__language__/Plugin__type__.__language__ at (5, 1)

      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD FAILURE
      [INFO] ------------------------------------------------------------------------
      [INFO] Total time: 59.361 s
      [INFO] Finished at: 2014-10-07T13:49:28+02:00
      [INFO] Final Memory: 15M/150M
      [INFO] ------------------------------------------------------------------------
      [ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2
      .2:generate (default-cli) on project standalone-pom: org.apache.maven.archetype.
      exception.ArchetypeGenerationFailure: Error merging velocity templates: Unable t
      o find resource 'Java/TestStep.inc' -> [Help 1]

      Delete
    2. hehe - please specify Java with all-small-caps; i.e. just "java" - does that help?

      /Ole

      Delete
    3. The new one :)

      [INFO] Using following parameters for creating project from Archetype: soapui-plugin-archetype:1.0
      [INFO] -------------------------------------------------------------------------
      ---
      [INFO] Parameter: groupId, Value: com.soapui.andhr
      [INFO] Parameter: artifactId, Value: average-test-step
      [INFO] Parameter: version, Value: 1.0.0
      [INFO] Parameter: package, Value: com.soapui.andhr
      [INFO] Parameter: packageInPathFormat, Value: com/soapui/andhr
      [INFO] Parameter: package, Value: com.soapui.andhr
      [INFO] Parameter: version, Value: 1.0.0
      [INFO] Parameter: groupId, Value: com.soapui.andhr
      [INFO] Parameter: type, Value: TestStep
      [INFO] Parameter: language, Value: java
      [INFO] Parameter: artifactId, Value: average-test-step
      [WARNING] Can not override property: com.smartbear.soapui:soapui:jar
      [WARNING] Can not override property: com.smartbear.soapui:soapui-pro:jar
      [INFO] Try to merge plugin configuration of plugins with id: org.apache.maven.plugins:maven-compiler-plugin
      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD FAILURE
      [INFO] ------------------------------------------------------------------------
      [INFO] Total time: 30.714 s
      [INFO] Finished at: 2014-10-07T15:19:08+02:00
      [INFO] Final Memory: 15M/150M
      [INFO] ------------------------------------------------------------------------
      [ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.2:generate (default-cli) on project standalone-pom: Don't override file c:\!WORK\SOA\SOAPUI\AverageReponseTime\average-test-step\src\main\java\com\soapui\andhr\ActionGroups.java -> [Help 1]

      Delete
    4. wow - that looks really strange; did it really prompt you for all those parameters multiple times!? did you clean out your current directory before running? perhaps you could clean out the archetype from your local repo as well!?

      /Ole

      Delete
    5. Yes! It works now, i forgot to clean up my directory... But now, when i open project in eclipse it shows 2 errors in PluginTestStep.java
      The import com.eviware.soapui.plugins.auto.PluginTestStep conflicts with a type defined in the same file
      Type mismatch: cannot convert from PluginTestStep to Annotation

      Probably smth wrong with my environment, but i have several more projects and they work just fine...

      Delete
    6. Great! Did you clear out your locally cached archetype? The error you are getting should be fixed in the latest version... perhaps I should bump the version number...

      /Ole

      Delete
    7. Huh :) It would be great if you can point me how to do this.

      Delete
    8. Forget about my previous comment. I've cleaned it up and now everything is fine. Thanks!

      Delete
    9. Great - thanks for hanging in there - I'm sorry you had to discover all these issues :-)

      Don't hesitate to ask for help regarding the actual TestStep...

      /Ole

      Delete
    10. Yes! Your help would be just in time :) I already spent several days trying to get this (and also created topic on soapui forum):
      I want to create test step that will get some info from all test step results e.g. response time for each of them and calculate average for the whole test suite. As i understand i have to access WsdlTestSuiteRunner and iterate thru all TestCaseRunners and TestStepResults. But how can i access WsdlTestSuiteRunner from public TestStepResult run (TestCaseRunner testRunner, TestCaseRunContext context)?

      Hope you will share some light on this. Thanks in advance :)

      Delete
    11. Ok. I figured it out:

      public TestStepResult run (TestCaseRunner testRunner, TestCaseRunContext context)
      {
      WsdlTestSuite testSuiteNew = (WsdlTestSuite) testRunner.getTestRunnable().getParent();
      for (TestCase testCase : testSuiteNew.getTestCaseList()) {
      for (TestStep step : testCase.getTestStepList()) {
      if (step instanceof RestTestRequestStep) {
      RestTestRequestStep requestStep = (RestTestRequestStep) step;
      if (requestStep.getTestRequest().getResponse() != null) {
      averageTime = requestStep.getTestRequest().getResponse().getTimeTaken();

      }
      }
      }
      }
      }.

      In case if someone will need this

      Delete
    12. Hi Andrei,

      Great work - sorry for not helping, I'm just swamped with the Ready! API release... Regarding your code it looks fine - you could exchange RestTestRequestStep for HttpRequestTestStep and use HttpRequestTestStep.getHttpRequest().getResponse() instead - which would make this cover Http/Rest/Soap teststeps.

      A separate concern could be that this will only capture the last response a certain teststep has handled - so if you are running loops or datasources in your testcase you would miss those responses -> you can access them by getting the testRunner for each executed TestCase instead (which exposes a getResults() collection containing TestStepResults for each time a TestStep was executed).

      /Ole

      Delete
    13. Great, thanks. I will join "Assuring API Quality with the Extensibility of SOAPUI and Ready! API" workshop in Stockholm, October 20, so can imagine you have a lot of stuff to prepare :) good luck!

      Delete
    14. Ole,

      I have a doubt on the above comment:
      "A separate concern could be that this will only capture the last response a certain teststep has handled - so if you are running loops or datasources in your testcase you would miss those responses -> you can access them by getting the testRunner for each executed TestCase instead (which exposes a getResults() collection containing TestStepResults for each time a TestStep was executed). "

      Could you please help me in explaining more in detail. Currently, I face this kind of situation where I need to get responses of data driven tests done through DataSources.

      /Samy

      Delete
  6. Hi Samy,

    can you elaborate a little more on your setup? Perhaps you could use a DataSink TestStep to capture the responses for individual requests in a DataDriven test?

    /Ole

    ReplyDelete
  7. Hi Ole,
    I have download your search plugin in github example and build the jar with maven, and then put it in the .soapuios/plugins folder, but the plugin failed to added to the soapui with the ERROR:
    2015-11-10 13:55:06,393 ERROR [SoapUI] An error occurred [The plugin 'C:\Users\lile10\.soapuios\plugins\soapui-search-plugin-1.0.0.jar' has unsigned class files.], see error log for details
    2015-11-10 13:55:06,393 ERROR [errorlog] java.lang.SecurityException: The plugin 'C:\Users\lile10\.soapuios\plugins\soapui-search-plugin-1.0.0.jar' has unsigned class files.
    java.lang.SecurityException: The plugin 'C:\Users\lile10\.soapuios\plugins\soapui-search-plugin-1.0.0.jar' has unsigned class files.
    at com.eviware.soapui.plugins.ProductBodyguard$JarVerifier.verify(ProductBodyguard.java:107)
    at com.eviware.soapui.plugins.ProductBodyguard.isKnown(ProductBodyguard.java:39)
    at com.eviware.soapui.plugins.PluginManager.loadPlugins(PluginManager.java:93)
    at com.eviware.soapui.DefaultSoapUICore.loadPlugins(DefaultSoapUICore.java:155)
    at com.eviware.soapui.DefaultSoapUICore.init(DefaultSoapUICore.java:133)
    at com.eviware.soapui.StandaloneSoapUICore.(StandaloneSoapUICore.java:38)
    at com.eviware.soapui.SoapUI$SoapUIRunner.run(SoapUI.java:721)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
    2015-11-10 13:55:06,393 WARN [SoapUI] Plugin 'soapui-search-plugin-1.0.0.jar' is not loaded because it hasn't been signed by SmartBear Software.

    ReplyDelete