cjose

Alfresco Process Services - Unit Testing # I

Blog Post created by cjose Employee on Oct 13, 2017

This blog is the first part of the two blog series around the work we Ciju Joseph & Francesco Corti did as part of Alfresco Global Virtual Hack-a-thon 2017

Hack-a-thon Project Description & Goal

Alfresco Process Services (APS) powered by Activiti has a standard way to develop custom java logic/extensions in your IDE. Typically the process models that often needs a lot of collaboration from many members of a team are developed in the web modeler of the product. From a packaging and versioning perspective, the process application, should be managed together with your java project. Since the role of unit tests is very critical during the lifecycle of these process artifacts it is important that we have good unit test coverage, testing the process models and custom java logic/extensions. The goal of this hack-a-thon project was to work on some unit test utilities and samples which can benefit the Alfresco community.

 

As part of this work we created two java projects (maven based) which are:

  1. aps-unit-test-utils - This is a utils project containing:
    1. Logic to automatically download the process app from an APS environment and make it available in your IDE/workspace.
    2. Activiti BPMN and DMN engine configuration xmls with all the necessary spring beans that you would need for your testing
    3. Mocks for OOTB (Out of the Box) APS BPMN stencils such as “Publish To Alfresco”, “REST call task” etc. Not all the components are mocked, but this gives you an idea on how to mock OOTB stencil components!
    4. Helper classes and custom assertions based on AssertJ library to help you quickly write tests over your process and decision tables.
  2. aps-unit-test-example - This project contains a lot of examples showing how to test your BPMN/DMN models and also, custom java logic associated with these models.

 

In this blog I’ll be walking you through the utils project and some of the main features available in this project

Project Features

Utility to fetch Process App from APS

One of the main features of this project is that it allows you to download(optionally) the process models that you have modeled in APS web modeler to your IDE during your local unit testing. Once you are happy with all your changes and unit test results, you can save those downloaded models into the version control repository. The reason why I highlighted the word “optionally” above is that, it is important that when you run your unit test in a proper CI/CD pipeline, you are unit testing the models that you have in your version control repository and avoid any other external dependencies.

 

The package com.alfresco.aps.testutils.resources in the project contains the classes responsible for downloading the process models from an APS environment into your IDE.

It is this method com.alfresco.aps.testutils.resources.ActivitiResources.get(String appName) which does this magic for you! You can invoke this method from your test classes at testing time to download and test your changes in web modeler. The method logic is:

  1. Read a property file named “activiti-resources.properties” containing APS environment and api details. Sample property file available at activiti-resources.properties
  2. If the app you are requesting is found on the server and if you have permissions to access the app, it is downloaded to your project under the path src/main/resources/app as a zip and then exploded into this directory. All the existing models will be deleted prior to the download and unzip
  3. From a unit testing perspective of the models, it is important we have the BPMN and DMN xmls available in the exploded directory under src/main/resources/app/bpmn-models and src/main/resources/app/decision-table-models respectively. When this method is successfully completed you will have those xmls in the respective directories ready for unit testing.

Configuration XMLs for the BPMN and DMN engines

Since Alfresco Process Services is a spring based webapp, we wrote all the utils, helper classes, examples etc using spring features. In this section, I’ll explain the two main configuration xmls present in src/main/resources directory that can be used to bootstrap the process engine and the dmn engine for unit testing.

  1. src/main/resources/activiti.cfg.xml:  Using this xml configuration a process engine is created with an in-memory h2 database. As you probably know there is a lot of configurations options available for Activiti process engine. If your test setup requires advanced configurations you should be able to do everything in this xml.
  2. src/main/resources/activiti.dmn.cfg.xml: This xml can be used to start the Activiti rule engine (DMN engine), again with an in-memory h2 database

Depending on the model that you are testing (BPMN/DMN), you can use one of the above configuration xmls to bootstrap the engine from your test cases.

Non-Engine Bean Configuration XML

src/main/resources/common-beans-and-mocks.xml: This xml can be used to configure any mock/real beans that are required for your process testing but not really part of the process/rule engine configuration. Those mock and non-mock beans are explained in the following subsections.

Mock Beans for OOTB APS BPMN Stencils

Since I think “mocks” are best explained with an example, please find below a process diagram where I’m using an OOTB APS “REST call task” stencil. I have also highlighted some of the other components in the APS editor that fall in this category of OOTB stencils.

 

In this example, from a unit testing perspective of OOTB components, you need to make sure a few things such as:

  1. This step is invoked upon a successful start of the process
  2. The expected response is set in your unit test so that you can continue with the next steps in the process.
  3. The configurations set on the model are successfully transferred to the BPMN XML upon export/deployment

However there are things that are not in-scope of unit testing of OOTB components:

  1. Test whether the REST API configured is invoked successfully - this is integration testing
  2. Testing of various configurations available on a REST task - this is the responsibility of Alfresco engineering team to make sure the configurations are working as expected

 

This is where we use mocks instead of the real classes that are behind these components. In order to create the mocks for these components, we need to first look at how these tasks look inside the deployed bpmn.xml. For example the bpmn equivalent of the above diagram is shown below:

<serviceTask id="sid-AB2E4A5F-4BF6-48BE-8FF1-CDE01687E69A" name="Rest Call" activiti:async="true" activiti:delegateExpression="${activiti_restCallDelegate}">
  <extensionElements>
    <activiti:field name="restUrl">
      <activiti:string><![CDATA[https://api.github.com/]]></activiti:string>
    </activiti:field>
    <activiti:field name="httpMethod">
      <activiti:string><![CDATA[GET]]></activiti:string>
    </activiti:field>
    <modeler:editor-resource-id><![CDATA[sid-8124CB5D-BD47-49CD-B013-F7FFB576DE8D]]></modeler:editor-resource-id>
  </extensionElements>
</serviceTask>

As you can see from the XML, the bean that is responsible for the REST call is activiti_restCallDelegate. This bean also has some fields named “httpMethod”, “restUrl” etc. Let’s now look at the mock class (given below) that I created for this bean. Since it is a mock class, all you need to do is create a java delegate with the field extensions that are present in the bpmn.xml.

package com.alfresco.aps.mockdelegates;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.activiti.engine.delegate.Expression;

public class RestCallMockClass implements JavaDelegate{
    
     Expression restUrl;
     Expression httpMethod;
     Expression baseEndpoint;
     Expression baseEndpointName;
     Expression requestMappingJSONTemplate;
    
     @Override
     public void execute(DelegateExecution execution) throws Exception {
          // TODO Auto-generated method stub
     }

}

Now that I have the mock class, next step is to create a mock bean using this class so that it is resolved correctly during unit test time. Given below is the mock bean configuration in src/main/resources/common-beans-and-mocks.xml corresponding to the above mentioned mock class.

<bean id="activiti_restCallDelegate" class="org.mockito.Mockito" factory-method="mock"> 
   <constructor-arg value="com.alfresco.aps.mockdelegates.RestCallMockClass" />
</bean>

You may have noticed that I am using a class called org.mockito.Mockito for the creation of the mocks. This is from the Mockito Library which is a great library for mocking!

I have created a few mock classes in this project which you can find in com.alfresco.aps.mockdelegates package. I have included them in the common-beans-and-mocks.xml file too. As you probably know APS contains a lot of OOTB stencils and this project contains only a very small subset. The point is, you should be able to mock any such OOTB beans using the above approach.

Common Beans

Any common beans (helper classes, utils etc) that you may require in the context of your testing can be added to common-beans-and-mocks.xml. Technically it can be separated from the mock xml, but for the sake of simplicity I kept it in the same xml.

Custom Assertions & Helper Classes

The classes in packages com.alfresco.aps.testutils and com.alfresco.aps.testutils.assertions are basically helper classes and assertions which can be re-used across all your process testing in an easy and consistent way. An approach like this will help reduce the unit test creation time and also help enforce some process modelling and unit test best practices. Highlighting some of the key features:

  1. The project contains an AbstractBpmnTest and AbstractDmnTest class which can be used as parent class to test your bpmn.xml and dmn.xml respectively.
  2. Includes a mock email transport which is set up in the AbstractBpmnTest. This can be used to test any email steps you have in the process.
  3. Custom Assertions using AssertJ Library on Activiti entities such as Task, ProcessInstance, DelegateExecution etc. Please note, the assertions I have created in this project is definitely not covering all possible assertion scenarios. However I think I have put a decent mix in there for you get started and you can add many more assertion methods depending on your test cases.  

 

Checkout the next blog Alfresco Process Services - Unit Testing # II to see the usage of some of these helper classes and assertions.

Conclusion

There are plenty of articles and blogs out there around unit testing best practices. So I’m not going to do that again here. However just wanted to stress one point with the help of an example: Do not mix integration testing with unit testing.

For example, a process containing the following steps Start → DMN (Rules) → Service Task → UserTask → End can be tested as three units

  1. Unit test for the process xml where you mock the “DMN” and “Service Task” steps
  2. Unit test for the DMN xml testing each rules in the DMN
  3. Unit test for the Service Task class

So, what next?

  • If you already have a good unit testing framework around your processes in APS, great, continue that way. Feel free to provide your feedback and contributions either as comments or as blogs here on community.
  • If you don’t have any unit testing around your processes in APS, I hope this article will help you get started. Feel free to fork it and make it your own at your organization. Good unit tests around your processes, rules, code etc will definitely help you in the long run especially when doing upgrades, migrations, change requests, bug fixes etc

 

Happy Unit Testing!

Outcomes