How to quickly test a workflow on alfresco

This guide will allow you to test the workflows created within an alfresco distribution without having to do any integration tests. The test can be performed directly as a unitary test without therefore having to start distributing alfresco.

To do this we will use alfresco-tests, a library that contains the alfresco services mocked and rebuilt just for testing. On this page, you will find the details of the library

Project Scheme

In the photo, we see the workflow inside alfresco that we are going to test inside our SDK 4.1.
First, let’s give a brief mention of the structure of the project in the SDK you see in the following photo:

The following elements are present:


pom.xml – The descriptor of the SDK project
SimpleProcess.bpmn – The activiti workflow descriptor
workflow-context.xml – collects the alfresco components used by the workflow
alfresco-tests-activiti-sample.properties – a set of properties used by the components and by the workflow itself
module-context.xml – The main descriptor of the alfresco module that calls properties and components
Generation.java – A sample alfresco component declared in the workflow-context.xml
SimpleModel.java – An interface with the namespaces and the used qnames

For details you can see the project in the github repository.

JUnit Test

Now we will write the junit test to put in the src/test/java path:

import org.alfresco.mock.test.activiti.AbstractActivitiForm;
...
public class SimpleActivitiTest extends AbstractActivitiForm {

     @Deployment(resources = { "alfresco/module/alfresco-tests-activiti-sample/workflow/SimpleProcess.bpmn" })
     public void testWorkflow() {

     }
}

AbstractActivitiForm class allows you to communicate with the alfresco-tests library and initialize the test environment.

When the test starts, it will read the Deployment annotation that specifies where the workflow descriptor is located.

We can override the init method to add our workflow variables and content nodes. Here a sample:

	@Override
	public void init(Map<String, Object> variables) {
		super.init(variables);
		ActivitiProcessEngineConfiguration activitiProcessEngineConfiguration = (ActivitiProcessEngineConfiguration) processEngineConfiguration;
		NamespaceService namespaceService = activitiProcessEngineConfiguration.getServiceRegistry()
				.getNamespaceService();
		namespaceService.registerNamespace("mycont", SimpleModel.STARTING_URI);
		String generationFolderName = "20191024_154711";
		NodeRef site = insertFolder(sites, "simple-site");
		NodeRef documentLibrary = insertFolder(site, "documentLibrary");
		NodeRef pdv = insertFolder(documentLibrary, "pdv");
		NodeRef pda = insertFolder(documentLibrary, "pda");
		NodeService nodeService = activitiProcessEngineConfiguration.getServiceRegistry().getNodeService();
		nodeService.setProperty(pda, SimpleModel.PROP_PDA_ID_COUNTER, 0);
		generationFolder = insertFolder(pdv, generationFolderName);
		Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
		try {
			properties.put(ContentModel.PROP_NAME, "contracts_" + generationFolderName + ".zip");
			properties.put(ContentModel.TYPE_BASE, QName.createQName("mycont", "contractsPdvCons", namespaceService));
			insertZip(generationFolder, "contracts_" + generationFolderName + ".zip", "document", "text",
					properties);
		} catch (IOException e) {
		}
		// AUTHENTICATION
		// Always reset authenticated user to avoid any mistakes
		identityService.setAuthenticatedUserId(USER_NAME);

		Search search = activitiProcessEngineConfiguration.getSearchScript();
		MockLogger logger = activitiProcessEngineConfiguration.getLoggerScript();
		variables.put("initiator", initiator);
		variables.put("search", search);
		variables.put("logger", logger);
		variables.put("mywf_starterPdA", "Human");
	}

Through the processEngineConfiguration variable we can take the alfresco ServiceRegistry and call all alfresco components we need as the search service or the node service.

We have utilities method to execute the main functions as the insertFolder and insertZip.

We can override methods to create test users or groups. Here a sample:

protected Initiator initiator = new Initiator();
public final static String CONTRIBUTORS = "contributors";
...	
@Override
public void initDemoUsers(IdentityService identityService) {
	createUser(identityService, ADMIN_USER_NAME, "Kermit", "The Frog", ADMIN_USER_NAME, ADMIN_USER_NAME + "@activiti.org", null, asList(CONTRIBUTORS, "user", "admin"), asList("birthDate", "10-10-1955", "jobTitle", "Muppet", "location", "Hollywoord", "phone", "+123456789", "twitterName", "alfresco", "skype", "activiti_" + ADMIN_USER_NAME + "_frog"));
	initiator.getProperties().put("firstName", "Gonzo");
	initiator.getProperties().put("lastName", "The Great");
	initiator.getProperties().put("userName", USER_NAME);
}

@Override
public void initDemoGroups(IdentityService identityService) {
	String[] assignmentGroups = new String[] { CONTRIBUTORS };
	for (String groupId : assignmentGroups) {
		createGroup(identityService, groupId, "assignment");
	}

	String[] securityGroups = new String[] { "user", "admin" };
	for (String groupId : securityGroups) {
		createGroup(identityService, groupId, "security-role");
	}
}

Here a sample of implementation of the test method using the activiti api:

public final static String ACTIVITY_KEY = "generationWorkflow";
...
public void testWorkflow() throws ParseException {
		Map<String, Object> variables = new HashMap<String, Object>();
		init(variables);
	ActivitiProcessEngineConfiguration activitiProcessEngineConfiguration = (ActivitiProcessEngineConfiguration) processEngineConfiguration;
	ServiceRegistry serviceRegistry = activitiProcessEngineConfiguration.getServiceRegistry();

	// Start process
	variables.put("mywf_endDatePdV", dateFormat.parse("Mar 16 00:00:00 CET 2020"));
	MockActivitiScriptNode activitiScriptNode = new MockActivitiScriptNode(generationFolder, serviceRegistry);
	ActivitiScriptNodeList activitiScriptNodeList = new ActivitiScriptNodeList();
	activitiScriptNodeList.add(activitiScriptNode);
	variables.put("mywf_relatedPdVFolder", activitiScriptNodeList);
	variables.put("mywf_startDatePdV", dateFormat.parse("Mar 14 00:00:00 CET 2018"));
	variables.put("bpm_workflowDescription", "mkkmkmkmk");
	ProcessInstance instance = runtimeService.startProcessInstanceByKey(ACTIVITY_KEY, variables);

	// execute the user task
	List<Task> selectedPdV = taskService.createTaskQuery().taskDefinitionKey("selectedPdV")
				.includeProcessVariables().list();
	assertEquals(1, selectedPdV.size());
	Task firstTask = selectedPdV.get(0);
	taskService.complete(firstTask.getId());

	// process terminated
	instance = runtimeService.createProcessInstanceQuery().active().processInstanceId(instance.getId()).singleResult();
	Assert.assertNull(instance);

	// one file is created by the workflow
	SearchService searchService = serviceRegistry.getSearchService();
	ResultSet resultQ = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
				SearchService.LANGUAGE_FTS_ALFRESCO, "PATH:\"pda/contracts_" + generationFolder.getId() + ".zip\"");
	NodeRef createdNodeRef = resultQ.getNodeRef(0);
	Assert.assertTrue("Added a zip file in the PDA folder", createdNodeRef.toString().endsWith(
"workspace/company_home/sites/simple-site/documentLibrary/pda/contracts_"
						+ generationFolder.getId() + ".zip"));

	// the file is inside the workflow/packages activiti folder
	resultQ = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_FTS_ALFRESCO,
				"PATH:\"pkg_919f220e-870a-4c56-ba11-5030ee5325f0/contracts_" + generationFolder.getId() + ".zip\"");
		createdNodeRef = resultQ.getNodeRef(0);
		Assert.assertTrue("File zip in the activiti folder",
				createdNodeRef.toString()
						.endsWith("workspace/workflow/packages/pkg_919f220e-870a-4c56-ba11-5030ee5325f0/contracts_" + generationFolder.getId() + ".zip"));
		end();
}

MockActivitiScriptNode allows to add alfresco objects in the execution environment of activiti as for example node references.

The complete test class can be found here. Add now the test descriptor test-module-context.xml in the src/test/resources path:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<import resource="classpath:test-activiti-context.xml" />
	<import resource="classpath:alfresco/module/${project.artifactId}/module-context.xml" />
</beans>

Install the Library

Now to make the test work we need to integrate the alfresco-tests library into the pom.xml. Simply in the dependencies we add:

<dependency>
	<groupId>it.vige</groupId>
	<artifactId>alfresco-tests</artifactId>
	<version>6.2.0-ea.6</version>
	<scope>test</scope>
</dependency>

The version number is dependent on the alfresco version. In this case, we are using alfresco 6.2.0-ea.

All is done. Now you can start the test