Form keys couldn't be resolved

Hi guys,

I wrote a JUnit test to test my process. This process includes a start form and user forms. So in the JUnit test I am going to deploy the process and the needed start form. But somehow the engine can’t find the form. If I run a query against the form engine the start form model is stored in there. Maybe I am doing something wrong and I would appreciate any help.

I hope I have posted all necessary information.

Project structure --> process and forms
src/main/resources/diagrams/IGeL_Prozessfluss.bpmn20.xml
src/main/resources/forms/startForm.form

Process

  • The form key of the start event is “startForm”.

Form

  • The “key” value of the form is defined as “key”:“startForm”

The props for the database connection (app.test.properties):

jdbc.url=jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1
jdbc.driver=org.h2.Driver
jdbc.username=sa
jdbc.password=

Before I am going to create the configuration for the process and form engine.

public class DeployProcessTest {

	private ProcessEngineConfigurationImpl cfgProcess;
	private FormEngineConfiguration cfgForm;	
private String formEngineName = "formengine";

//Logging
private static Logger log = Logger.getRootLogger();



	@Before
public void initTests() {

	//TODO: Move to own method or class to read the properties
	Properties appProperties = new Properties();
	FileInputStream fileInStream = null;
	ClassLoader classLoader = new ProcessIgelApplication().getClass().getClassLoader();
	File propFile = new File (classLoader.getResource("app.test.properties").getFile());
	
	try {
		fileInStream = new FileInputStream(propFile);
	} catch (FileNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	try {
		appProperties.load(fileInStream);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

	//Creating a configuration for the process engine
	cfgProcess = (ProcessEngineConfigurationImpl) new StandaloneProcessEngineConfiguration()
			.setJdbcUrl(appProperties.getProperty("jdbc.url"))
			.setJdbcUsername(appProperties.getProperty("jdbc.username"))
			.setJdbcPassword(appProperties.getProperty("jdbc.password"))
			.setJdbcDriver(appProperties.getProperty("jdbc.driver"))				 
                         .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
	
	//Creating a configuration for the form engine
	cfgForm = (FormEngineConfiguration) new StandaloneFormEngineConfiguration()
			.setEngineName(formEngineName)
			.setJdbcUrl(appProperties.getProperty("jdbc.url"))
			.setJdbcUsername(appProperties.getProperty("jdbc.username"))
			.setJdbcPassword(appProperties.getProperty("jdbc.password"))
			.setJdbcDriver(appProperties.getProperty("jdbc.driver"))
		.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);
	
	
	
	//Add form engine to process engine configuration
	cfgProcess.addConfigurator(new FormEngineConfigurator()
                      .setFormEngineConfiguration(cfgForm));
	
}

I initialize the process engine and adding a form engine to it (code below).
Now the code of the unit test:

@Test
public void deployAndStartIgelProcessTest() {
		log.debug("PROCESS ENGINE BUILD");
	ProcessEngine processEngine = cfgProcess.buildProcessEngine();

	FormEngine formEngine = FormEngines.getFormEngine(formEngineName);
	
	log.debug("Form engine retrieved --> " + formEngine.getName());
	
	assertNotNull(processEngine);
	assertNotNull(formEngine);
	assertEquals(formEngineName, formEngine.getName());
	
	//Get flowable services
	RepositoryService repositoryService = processEngine.getRepositoryService();
	RuntimeService runtimeService = processEngine.getRuntimeService();
	TaskService taskService = processEngine.getTaskService();
	HistoryService historyService = processEngine.getHistoryService();
	FormRepositoryService formRepositoryService = formEngine.getFormRepositoryService();
	FormService formService = processEngine.getFormService();
	
	//TODO: create own class / method to do this
	//Deploy forms
	String formDeploymentId = formRepositoryService.createDeployment()
		.name("startForm")
		.addClasspathResource("forms/startForm.form")
		.deploy()
		.getId();
	
	assertNotNull(formDeploymentId);
	
	
	//Retrieves the correct Model startForm
	FormModel test = formRepositoryService.getFormModelByKey("startForm");


//Deploy process
	String deploymentId = repositoryService.createDeployment()
		.addClasspathResource("diagrams/IGeL_Prozessfluss.bpmn20.xml")
		.deploy()
		.getId();
	
			
	assertNotNull(deploymentId);
	

	ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("igel_process");
	
	log.debug("Started process instance --> " + processInstance.getName());
	
	ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
			.deploymentId(deploymentId)
			.singleResult();
	
	assertNotNull(processDefinition);
	
	log.debug("Process Definition: " + processDefinition);
	
	
	Map<String,Object> processVariables = runtimeService.getVariables(processInstance.getId());
	
	log.debug("Process vars :" + processVariables);
	
	//TODO: Insert assertion for process variables
	
	//Get form data
	
	//TODO: Form Service genauer analysieren
	log.debug("STARTFORM DATA --> " + formService.getStartFormData(processDefinition.getId()));
	log.debug(formService.getStartFormData(processDefinition.getId()));

	
	List<HistoricTaskInstance> historicInstance = historyService.createHistoricTaskInstanceQuery().list();
	
	List<HistoricTaskInstance> historicTasks = historyService.createHistoricTaskInstanceQuery().finished().list();
	
	
	
	List<Task> taskQueryResult = taskService.createTaskQuery().list();
	
	Object startForm = formService.getRenderedStartForm(processInstance.getProcessDefinitionId());
	
	processEngine.close();
	formEngine.close();

But he can’t find the corresponding start form, with

Object startForm = formService.getRenderedStartForm(processInstance.getProcessDefinitionId());

I get the following exception:

org.flowable.engine.common.api.FlowableObjectNotFoundException: Form with formKey 'startForm' does not exist
at org.flowable.engine.impl.form.JuelFormEngine.getFormTemplateString(JuelFormEngine.java:71)
at org.flowable.engine.impl.form.JuelFormEngine.renderStartForm(JuelFormEngine.java:43)
at org.flowable.engine.impl.cmd.GetRenderedStartFormCmd.execute(GetRenderedStartFormCmd.java:69)
at org.flowable.engine.impl.interceptor.CommandInvoker$1.run(CommandInvoker.java:51)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:93)
at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:72)
at org.flowable.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:56)
at org.flowable.engine.impl.interceptor.BpmnOverrideContextInterceptor.execute(BpmnOverrideContextInterceptor.java:25)
at org.flowable.engine.common.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:53)
at org.flowable.engine.common.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:72)
at org.flowable.engine.common.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:35)
at org.flowable.engine.common.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:56)
at org.flowable.engine.common.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:51)
at org.flowable.engine.impl.FormServiceImpl.getRenderedStartForm(FormServiceImpl.java:38)
at de.northernlights.igel_process.test.DeployProcessTest.deployAndStartIgelProcessTest(DeployProcessTest.java:264)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)

Many thanks in advance,
Thomas

1 Like

Hi,

I thinks its due to seperate deployment of form and process. Try deploying a bar file (can be downloaded from flowable modeler) or try deploying form and process in same deployment.

See this comment :

Thank You

FYI: a .bar (or .zip) will work indeed. Alternatively, you can also add the form resource to the deploymentBuilder of the process, thus doing two calls to .addClasspathResouurce, once with the process, the other with the form resource.

However, is this form a custom form or does it come from the Flowable Modeler?

1 Like

Hi arpit.agrawal and joram,

thank you for your help. So I tried both suggested ways to deploy the process and the forms. Both work well, so I am going to use the bar deployment.

The call of formService.getRenderedStartForm(processInstance.getProcessDefinitionId()); doesn’t work at all, but I think I made a mistake there. If I am right it’s for usage with an external form renderer. At the moment I am usind the build in forms.

@joram
I created the forms with the flowable modeler, downloaded them and included them in my eclipse project. For me it was the easiest way instead of creating them manually. :slight_smile:

Offtopic:
Is there maybe some kind of documentation regarding the form json properties, like layout, etc. Therefore I couldn’t find anything.

Thank you
Thomas

Hi Thomas,

Yes the getRenderedStartForm is meant to the form properties you can define on a user task or start event. For the JSON based forms and form renderer in the Task application you can use RuntimeService getStartFormModel and TaskService getTaskFormModel

Best regards,

Tijs

1 Like

This is a helpful post. I’ve been struggling with end 2 end testing the process and forms. Testing process model is well documented and easy to follow. But testing both process and forms is not documented at all, and the documentation and javadocs on form testing is light, confusing and even incorrect. For example:

https://www.flowable.org/docs/userguide-form/#apiUnitTesting

FormService formService = dmnEngine.getFormService();

where does dmnEngine come from? How does the form instance f7689f79-f1cc-11e6-8549-acde48001122 get deployed?

JavaDocs for FlowableFormRule start with:

Convenience for DmnEngine and services initialization in the form of a JUnit rule

DmnEngine?

And then:

You can declare a deployment with the {@link FormDeploymentAnnotation} annotation.

Sounds great. But a code sample of this? Tried specifying a .form resource but to no avail.

The reason is because for the engine, forms are just variables. The engine actually knows very little of forms and that’s why it’s not described. The form model is a representation of a json model. The model is used to define the names of the variables that should be stored. It’s true that there are not many example for this - as the main use case is the Flowable UI’s. Users that embed the engine and not use the UI’s typically use their own form technology.

I assume you’re talking about the default form engine that is exposed in the Flowable Modeler, not your own form technology with using the formKey?

The form engine is used by various engines. So the DMN engine using it, is one example. The process engine also uses it.