Creating your own TestSteps in soapUI

Creating your own TestSteps in soapUI
Ole Lensmar
  January 30, 2012

This time around let’s do something really low-level and useful: use the soapUI extensions mechanisms to create a custom TestStep for sending an email during the execution of a TestCase. Since we have quite a lot of ground (and code!) to cover, let’s get started.

Short Overview

soapUI internally uses a number of registries containing factories for core objects, for example TestSteps, assertions, desktop panels etc. (see Custom Factories for an extensive list). To create a custom TestStep, we therefore need to supply at least one factory: that for creating the TestStep itself – a TestStepFactory. Additionally (but not required) since we also want to provide a nice panel inside soapUI for editing the actual email and related properties we need to provide another factory for creating the PanelBuilder that creates this editor panel – a PanelBuilderFactory.

soapUI makes heavy use of the apache XMLBeans framework for holding configurations for internal objects – these are generated from the soapui.xsd file included in the soapUI source code. You will see how the state of our TestStep is read and written to objects created for this purpose a bit further down.

Confusing? Don’t worry – and to be honest, looking at this code that has been written over the last seven years, there are many things that could have been done differently (and better for sure) – bear with us!

1. Create and register the TestStep Factory and TestStep class

As mentioned, the TestStep itself is created by a TestStepFactory – a WsdlTestStepFactory to be exact (legacy naming…) – here it is:

public class EMailTestStepFactory extends WsdlTestStepFactory

{

private static final String EMAIL_STEP_ID = "email";

public EMailTestStepFactory()

{

super( EMAIL_STEP_ID, "EMail TestStep", "Sends an email", "email.png" );

}

public WsdlTestStep buildTestStep( WsdlTestCase testCase, TestStepConfig config,

 boolean forLoadTest )

{

return new EMailTestStep( testCase, config, forLoadTest );

}

public TestStepConfig createNewTestStep( WsdlTestCase testCase, String name )

{

TestStepConfig testStepConfig = TestStepConfig.Factory.newInstance();

testStepConfig.setType( EMAIL_STEP_ID );

testStepConfig.setName( name );

return testStepConfig;

}

public boolean canCreate()

{

return true;

}

}

Let’s have a quick look at the methods:

  • The constructor invokes the super constructor with a number of basic properties, including the unique ID for the TestStep and the path to an icon for representing the TestStep (thanks for FamFamFam!). The path can be either absolute or relative to the current working directory.
  • The createNewTestStep method is called by soapUI when the user selects to create a new TestStep of our type, it returns an XmlBeans TestStepConfig (from the corresponding TestStep complex type in the soapUI XSD) that will be the basis for the state of the TestStep and its configuration
  • The buildTestStep method is called to create the actual TestStep – it is passed the containing TestCase (TestSteps must always be in a TestCase) and the corresponding TestStepConfig object that was at some time created by the above createNewTestStep method.
  • The canCreate method simply tells soapUI that the factory is working ok – here you could add a check of some external conditions required for this factory to be active, for example a license-check.

Of course we also need to create the EMailTestStep class returned by the buildTestStep method:

public class EMailTestStep extends WsdlTestStepWithProperties

{

protected EMailTestStep( WsdlTestCase testCase, TestStepConfig config, boolean forLoadTest )

{

super( testCase, config, true, forLoadTest );

if( !forLoadTest )

{

setIcon( UISupport.createImageIcon( "email.png" ) );

}

}

@Override

public TestStepResult run( TestCaseRunner testRunner, TestCaseRunContext context )

{

WsdlTestStepResult result = new WsdlTestStepResult( this );

result.setStatus( TestStepStatus.OK );

return result;

}

}

This is almost the simplest possible implementation;

  • The constructor passes all arguments to its super-class and initializes the icon if we are not running under a LoadTest (either in soapUI or loadUI) – when the icon isn’t needed anyway.
  • The run method is what is called by soapUI during TestCase execution – our default implementation just creates a default result object, sets its status to OK and returns it.This is where we will do the actual sending of the email a bit further down.

These two classes are actually enough to get things started in soapUI, all that is missing is an xml configuration file that adds the factory to soapUI – it should be placed in the soapUIbinfactories folder and named anything ending with -factories.xml (we will call it email-teststesp-factories.xml). It contains the following:

factoryType="com.eviware.soapui.impl.wsdl.teststeps.registry.WsdlTestStepFactory"

factoryClass="soapui.demo.teststeps.email.EMailTestStepFactory"/>

As you can see this simply registers our factory of type WsdlTestStepFactory with soapUI.

Great! After compiling and packaging these classes into a jar file and dropping them into the soapUIbinext folder together with the factories xml file in the factories folder we can start soapUI – and the magic begins!

On startup you should see the following rows in the soapUI log at the bottom of the soapUI window:

Wed Jan 25 00:03:51 CET 2012:INFO:Adding factories from [C:..factoriesemail-teststep-factories.xml]

Wed Jan 25 00:03:51 CET 2012:INFO:Adding factory [class soapui.demo.teststeps.email.EMailTestStepFactory]

Awesome! Now if we create an empty project and just add a TestSuite and TestCase we will see the following in the TestCase window:

teststeps-toolbar

And pressing that button (or using the right-click menu in the empty list of TestSteps ) allows us to add an Email TestStep:

Created-teststep

Finally, running the TestCase will show the following in the TestCase Run Log at the bottom of the TestCase window:

initial-testrun-log

Great! Of course we can save / reload the project, add more TestSteps, move them around, clone them, etc. – everything that the soapUI infrastructure allows now also supports our (totally useless) TestSep.

As a last improvement before we add the desktop panel, let’s fix the right-click menu of the TestStep. Currently it looks like this:

Original-right-click-menu

This is missing some key actions, including the actions to open the TestSteps editor panel (which will create below), enable/disable the TestStep, get help, etc. < /p>

What we need to do is create a corresponding popup menu definition for our TestStep in a custom email-teststep-actions.xml file that we save to the soapUIbinactions folder (which plugs into the actions infrastructure in soapUI):

id="EMailTestStepActions"

class="com.eviware.soapui.impl.wsdl.actions.teststep.WsdlTestStepSoapUIActionGroup">

keyStroke="F1" param="https://blog.smartbear.com" />

Here we define a soapUI “action-group” which is used to populate the popup menu above – the id of the group matches the actual name of the TestStep class and it uses a custom SoapUIActionGroup implementation internally (which adds these actions and an “Open Editor” action to the popup you can see above).  We only add a custom Separator and Online Help action – the custom WsdlTestStepSoapUIActionGroup does the rest. Save the file and restart soapUI – you will now see the following row in the soapUI log:

INFO:Adding actions from [C:..actionsemail-teststep-actions.xml]

Now that we have this file registered the right-click menu looks a little nicer:

 

Time to move on!

2. Create a PanelBuilderFactory and its DesktopPanel

The next step is to create a similar skeleton for the desktop panel that we want to show to our users when they double-click the TestStep (which for now does nothing). Once again we will create a Factory, this time for a PanelBuilder that is used by soapUI to build both the main desktop panel and the properties panel in the bottom left for our new TestStep. Let’s start with the Factory and Builder first:

public class EMailTestStepPanelBuilderFactory implements 

PanelBuilderFactory

{

@Override

public PanelBuilder createPanelBuilder()

{

return new EMailTestStepPanelBuilder();

}

@Override

     public Class getTargetModelItem()

{

return EMailTestStep.class;

}

public static class EMailTestStepPanelBuilder extends

EmptyPanelBuilder

{

@Override

public DesktopPanel buildDesktopPanel( EMailTestStep modelItem )

{

return new EMailTestStepDesktopPanel( modelItem );

}

@Override

public boolean hasDesktopPanel()

{

return true;

}

}

}

This is once again pretty straight forward – the EMailTestStepPanelBuilderFactory creates the corresponding PanelBuilder in the createPanelBuilder method, and the EMailTestStepPanelBuilder is implemented as an inner class which simply creates the corresponding EMailTestStepDesktopPanel when asked to. Also, it overrides the hasDesktopPanel() method to return true to let soapUI know that it has this functionality.

Of course we need to create the actual EMailTestStepDesktopPanel returned by the PanelBuilder:

public class EMailTestStepDesktopPanel extends ModelItemDesktopPanel

{

public EMailTestStepDesktopPanel( EMailTestStep modelItem )

{

super( modelItem );

}

}

No frills for now – very simple – we’ll spice it up further down the line.

Just as for the TestStep Factory, we need to add this PanelBuilder Factory to our email-teststep-factories.xml file, which now contains two factories:

   

   factoryType="com.eviware.soapui.impl.wsdl.teststeps.registry.WsdlTestStepFactory"

   factoryClass="soapui.demo.teststeps.email.EMailTestStepFactory"/>   

   

   factoryType="com.eviware.soapui.model.util.PanelBuilderFactory"

   factoryClass="soapui.demo.teststeps.email.EMailTestStepPanelBuilderFactory"/>   

Great! Package this up and restart soapUI – the soapUI log will now contain:

INFO  [DefaultSoapUICore] Adding factories from [C:..email-teststep-factories.xml]

INFO [DefaultSoapUICore] Adding factory [class soapui.demo.teststeps.email.EMailTestStepFactory]

INFO [DefaultSoapUICore] Adding factory [class soapui.demo.teststeps.email.EMailTestStepPanelBuilderFactory]

Now double-clicking on one of our Email TestSteps will open an empty window:

And in the bottom left you can see the default table of properties created by the base EmptyPanelBuilder class. Try changing the name of the TestStep there and watch what happens to the title in the opened panel. Fabulous!

3. Implement configuration handling

Now that we have the core plumbing of our TestStep in place let’s add some actual logic. First we need to define the properties that our TestStep will use:

  • Subject – the subject of the email message to send
  • Message – the actual message content
  • Server – which SMTP server to use
  • MailFrom – the from address
  • MailTo – and the to address

(Of course this could be extended with many more – I’ll leave that to you).

We also need to add code for reading/writing these properties from/to the underlying XMLBeans configuration object, main points to adding this are as follows:

private String subject;

private String message;

private String server;

private String mailTo;

private String mailFrom;

private void readConfig( TestStepConfig config )

{

XmlObjectConfigurationReader reader = new XmlObjectConfigurationReader( config.getConfig() );

subject = reader.readString( "subject", "" );

  message = reader.readString( "message", "" );

server = reader.readString( "server", "" );

mailTo = reader.readString( "mailTo", "" );

mailFrom = reader.readString( "mailFrom", "" );

}

private void updateConfig()

{

XmlObjectConfigurationBuilder builder = new XmlObjectConfigurationBuilder();

builder.add( "subject", subject );

builder.add( "message", message );

builder.add( "server", server );

builder.add( "mailTo", mailTo );

builder.add( "mailFrom", mailFrom );

getConfig().setConfig( builder.finish() );

}

public String getSubject()

{

return subject;

}

public void setSubject( String subject )

{

String old = this.subject;

this.subject = subject;

updateConfig();

notifyPropertyChanged( "subject", old, subject );

}

This is pretty straight-forward:

  • We define the properties as private fields
  • We add a “readConfig” method that is used to read the values in the configuration into our properties
  • We add an “updateConfig” method that saves all properties back to the underlying XMLBeans configuration object
  • We call this updateConfig method in all property setters before notifying property changes.

4. Add UI

Of course there wouldn’t be much fun if we couldn’t edit these fields in the DesktopPanel that we created previously – I’ll save you the code (download it below) for now – the result is as follows:

This is probably the messiest code to get to grips with as it uses a lot of internal soapUI utility classes for building Swing interfaces (forms, etc) – you are of course in no way forced to use those, if you prefer to use your own Swing components you are free to do just that (or how about embedding JavaFX 2?).

5. Implement run method

Almost there now – we just need to add the actual code for sending the email to our run method in the TestStep:

 

public TestStepResult run( TestCaseRunner testRunner, TestCaseRunContext context )

{

WsdlTestStepResult result = new WsdlTestStepResult( this );

result.startTimer();

try

{

Properties props = System.getProperties();

props.put( "mail.smtp.host", context.expand( server ) );

Session session = Session.getDefaultInstance( props, null );

Message msg = new MimeMessage( session );

msg.setFrom( new InternetAddress( context.expand( mailFrom ) ) );

msg.setRecipients( Message.RecipientType.TO, InternetAddress.parse(

context.expand( mailTo ), false ) );

msg.setSubject( context.expand( subject ) );

msg.setText( context.expand( message ) );

msg.setHeader( "X-Mailer", "soapUI EMail TestStep" );

msg.setSentDate( new Date() );

Transport.send( msg );

result.setStatus( TestStepStatus.OK );

}

catch( Exception ex )

{

SoapUI.logError( ex );

result.setError( ex );

result.setStatus( TestStepStatus.FAILED );

}

result.stopTimer();

return result;

}

This is actually very straight forward javamail code – fortunately soapUI already includes the required libraries (it needs them for mime attachment handling). Now if we send the above mail by running the TestCase we get the following in the log:

and eventually the following in our mailbox:

Sweet 🙂

One thing to note is that we used context.expand( ) to assign the corresponding properties on the MimeMessage object; this expands any property-expansions in the corresponding values, thus effectively adding property-expansion support to all fields in the TestStep. This does come at a small price though; the TestStep needs to implement the PropertyExpansionContainer interface which is used by the refactoring engine in soapUI to update property-expansions when properties or contracts are updated. Fortunately this interface only mandates one method:

 

public PropertyExpansion[] getPropertyExpansions()

{

List result = new ArrayList();

result.addAll( PropertyExpansionUtils.extractPropertyExpansions( this, this, "subject" ) );

result.addAll( PropertyExpansionUtils.extractPropertyExpansions( this, this, "message" ) );

result.addAll( PropertyExpansionUtils.extractPropertyExpansions( this, this, "server" ) );

result.addAll( PropertyExpansionUtils.extractPropertyExpansions( this, this, "mailTo" ) );

result.addAll( PropertyExpansionUtils.extractPropertyExpansions( this, this, "mailFrom" ));

return result.toArray( new PropertyExpansion[result.size()] );

}

This is easy to implement thanks to a bunch of internal utility methods used for extracting the PropertyExpansions in our TestStep.

Let’s put the property-expansion support to test:

This delivers the following mail when running the TestCase:

Finally let’s check how errors are handled – enter a bogus mailHost address and run the TestCase. You should get the following:

6. Wrap-up

That’s it! We’ve created a simple Email TestStep showing how easy it is to extend soapUI with new functionality. Let’s recap:

– We created a custom WsdlTestStepFactory which provides our new EMailTestStep

  • EmailTestStepFactory.java
  • EMailTestStep.java

– We created a custom PanelBuilderFactory which provides the PanelBuilder used by soapUI to build the desktop and property panels in the UI

  • EMailTestStepPanelBuilderFactory.java

-We created the EMailTestStepDestopPanel (provided by our EMailTestStepPanelBuilder) that provides the actual UI for the EMail TestStep

  • EMailTestStepDesktopPanel.java

-We created two configuration files – one for registering these two factories and one for the right-click menu of the TestStep

  • email-teststep-factories.xml
  • email-teststep-actions.xml

-We added an icon for the TestStep to the root folder of soapUI

  • email.png

That makes seven files all in all – not too bad – when packaging we need to:

  • compile the java files into a jar that we drop into the soapUIext folder
  • put the factories xml file in the soapUIfactories folder
  • put the actions xml file in the soapUIactions folder
  • put the png in the root folder

An obvious improvement in soapUI would be to support a “plugin-file” format which packs all these into just one file – something for a future release. 🙂

Next Steps

This was obviously a very basic example; a lot can be improved:

  • Add more configuration possibilities for the actual email; attachments, multiple email addresses, etc.
  • Add a Run button to the panel to allow instant sending of emails.
  • Add support for sending HTML emails.
  • Add support for a script that decides if the email should be sent at all
  • etc…

Download the zip for the source files, configuration files and email.png icon and use it as an inspiration to create your own TestSteps – and please don’t hesitate to mail any TestSteps you create to us so we can publish them on the soapUI website for everyone to enjoy and use!

Also please download the latest nightly build to have all the latest fixes (some related to icon handling in the above example) – and don’t hesitate to get in touch in the forum if you have any questions or issues.

As always – Thanks for your time!

/Ole

SmartBear Software

 See also:

You Might Also Like