Testing custom Alfresco Services and Actions with JUnit

Welcome to the under appreciated world of Quality Assurance! In this tutorial I’ll be going over the basics of writing automated test for an Alfresco SDK project for SDK version 2.2, Alfresco 5.1. The code for this example can be found here.

Alfresco AIO are generated with JUnit as a dependency, so you likely won’t need to edit any pom files, unless you’re looking to change version. The folder containing test materials can be found in the repo-amp src folder.

For this tutorial we’ll being using a simple service called NodeMaker which creates a content node under a specified parent node. We’ll also use an Action named NodeAction which changes the owner of a specified node.

First things first, we’ll briefly go over the anatomy of a test class.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:alfresco/application-context.xml")
public class NodeMakerTest {
//Your tests
}

This is the standard header of any test class. It loads a number of annotations as well as the application context, which gives access to all of Alfresco’s services. The @Remote annotation is also commonly used, but since we’re running the test locally, we won’t need it.

static Logger log = Logger.getLogger(NodeMakerTest.class);

Our good friend the Logger. Alfresco has a bit of an issue with log.debug() not being enabled for test classes out of the box. To remedy that, go to the extension folder in the test directory and create a file called “dev-log4j.properties” as shown below.

Now, just add a line setting the logging level for your custom code’s classpath to debug.  In my case, I added:

log4j.logger.com.fika=debug

Back to the test class!

@Autowired
@Qualifier("NodeService")
protected NodeService nodeService;

Autowiring grabs a bean from the application context and automatically sets up an instance of the class. Note that autowiring your new service may generate an error in and IDE depending on how “smart” it is.

The Qualifier is not strictly necessary in our case, but, if you create twos bean from a the same class with pre-loaded properties, this is how you go about calling the specific instance you want. It won’t be necessary for the custom service.

Now, onto the tests!

@Test
public void testWiring() {
    assertNotNull(nodeService);
    assertNotNull(nodeMaker);
    assertNotNull(contentService);
    assertNotNull(nodeLocatorService);
}

This is the first test of most test classes, this will ensure that all of the services and components are loaded.
There are also @Before and @BeforeClass annotations. The former method would be executed before every test, while the latter will be executed only before the first test. In JUnit 5 these have been changed to @BeforeEach and @BeforeAll for clarity.

That’s it for the basics, lets move on to the example.

First I created the NodeMaker service and NodeAction in in the com.fika.demonode package.

Then, I created my test class in the test folder for the package.

Actions are pieces of repeatable code that can be executed from Folder Rules, Workflows, Web Scripts, and Scheduled Jobs. Complexity varies, but they all follow a basic format, and of course, testing them is essential.

This isn’t going to be full tutorial on actions, but I’ll include a link to one at the end of the article. This example will just show the basics on how they can be executed from a test class. You may end up testing someone else’s Actions, and knowing a couple of key things can save you some time.

Every Action class extends the ActionExecuterAbstractBase class and contains two functions,

protected void executeImpl(Action action, final NodeRef actionedNodeRef)

And addParametersDefinition, which looks like this in our example:

//com.fika.demonode.NodeAction.java
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList){
    paramList.add(
    new ParameterDefinitionImpl(
    PARAM_OWNER,   		                //  The parameter name, defined earlier in the class definition
    DataTypeDefinition.TEXT,            //  The parameter data type.
    True,			                    //  Whether the parameter is mandatory.
    getParamDisplayLabel(PARAM_OWNER)));//  The parameter label.
}

For this example we only had a single parameter. You can, of course, add as many as you need. I’ll also provide a link to Alfresco’s documentation at the end of the article.

In this case the Action is very simple. We call the Ownable service and change the owner of the node we created.

When testing an action, you’ll need to do some extra work for the system to allow you to execute it. That code can be seen below.

//com.fika.demonode.NodeMakerTest.java
RetryingTransactionHelper transactionHelper = transactionService.getRetryingTransactionHelper();
try {
        transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
        {
         public Void execute() throws Exception {
              AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>() {
                  @Override
                  public Void doWork() throws Exception {
                     log.debug("Executing NodeAction");
                     Action action = actionService.createAction("NodeAction"); //The bean Id
                     HashMap<String, Serializable> param = new HashMap<String,Serializable>();
                     param.put("alfUser", NEW_NODE_OWNER);
                     action.addParameterValues(param);
                     actionService.executeAction(action, testNode);
                     return null;
                     }
                  }, AuthenticationUtil.getSystemUserName());
                 return null;
              }
            },
            false, true);
    }
catch (Exception e)
    {log.error("There was an errorin transaction");}

There’s a lot going on here, but all the wrapper does is temporarily provide you with the credentials to perform an Action in Alfresco. At its core the only content that needs to be changed is the content of the doWork() function. As a warning, this wrapper is immensely powerful, so use it wisely.

To run your tests you can navigate to your project in the terminal and run the command “mvn clean test”. You can also append additional arguments to this command:

Run a specific test class:                                     -Dtest=NodeMakerTest
Run a single test from a class:                            -Dtest=NodeMakerTest#testWiring
Do not report failures for amps with no tests:      -DfailIfNoTests=false
Only run tests in a specific amp:                          -pl [your-amp]

Note that if you’re running a -P command (a profile), the profile may have the “skipTests” property set to true.

That concludes the very basics of testing your custom Alfresco services and actions using JUnit. I hope it saves you some time. Happy testing!

If you’re looking for the documentation for the maven surefire plugin it can be found at:
http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html

Here’s a link to Jeff Potts’ tutorial on custom actions:
http://ecmarchitect.com/alfresco-developer-series-tutorials/actions/tutorial/tutorial.html

Finally, another example of an Alfresco action class:
http://docs.alfresco.com/5.1/references/dev-extension-points-actions.html

 

Leave a Reply

Your email address will not be published. Required fields are marked *

From our Blog...

Configuring Alfresco SAML SSO Module with Okta IdP

Alfresco recently released a new patch for their SAML Single Sign On solution module. This module allows Alfresco user’s to configure their Alfresco installation with their Single Sign On (SSO) Identity Provider. In this tutorial, I’ll explain the process of configuring Okta to be used with the module. Note: This tutorial is assuming you’ve followed… Read more »

Read More

Content Migration: Being Prepared

Much like negotiating a treaty between two countries who do not share a common language, someone will be faced with the task of translating. If that translator is not properly prepared the outcome might create more problems than it solves.

Read More

Debugging and Integration Testing in Alfresco SDK 3.0

Alfresco has updated its SDK! See our articles here and here about the basics. In its current state, SDK 3.0 doesn’t support unit testing. It does, however, have a robust Integration Testing framework which, in many ways, covers the same ground and then some. In this article I’ll be going into the basics of Integration… Read more »

Read More