Stop tasklist forms from creating new process variables?


#1

Hi folks,

We are building our workflows currently using embedded forms within the standard Camunda task UI. This works OK, but it causes a bit of a gap in our unit tests, because the tests have no idea about embedded forms.

When we want to test a user task, we use the TaskService to complete a Task with the variables we expect a user to enter - but there’s no way to be sure these variables are actually the ones created in the embedded form without either manual testing, or a very complex (and low value) browser test against the form.

One thing I was wondering is - could we block forms from creating any new process variables? I.E let the forms update existing variables, but not create new ones? So we’d create all our variables in a start task or script task earlier, and enforce that forms could modify them only.

This would at least narrow the range of possible bugs where someone mis-spells a form field ID, and everything silently works because the form makes a brand new variable that is then ignored by the rest of the workflow.

Or is there some other cunning way people test task forms without loading the entire UI in a set of Selenium tests?


#2

Hi @Korny,

one idea that came into my mind: you can assert the formkey in the JUnit test with the latest version of the camunda-bpm-assert library: https://github.com/camunda/camunda-bpm-assert/blob/master/camunda-bpm-assert/User_Guide_BPMN.md#task-hasFormKey.

Then you can provide some helper methods in your unit tests to read the form as a file and try simple regexpression to seach for cam-variable-name in the file and compare them to your variable list in the tests.

Do you think this is a valid approach?

Cheers, Ingo


#3

Hmm - that’s an interesting idea - at minimum we could whitelist valid form variables that way.


#4

It works - for reference, this is the code:

  private Set<String> getFormFieldsFromHtml(Task task) {
    String formKey = getTaskFormKey(task)
    if (!formKey.startsWith("embedded:app:")) {
      throw new RuntimeException("Non-embedded forms not yet supported: '" + formKey + "'");
    }
    String formResourceLocation = formKey.replaceFirst("embedded:app:", "static/");
    try {
      String formText =
          Resources.toString(Resources.getResource(formResourceLocation), Charsets.UTF_8);
      // sadly doing this with streams needs Java 9
      Set<String> formFields = new HashSet<>();
      Pattern camundaVariableNamePattern =
          Pattern.compile("cam-variable-name\\s*=\\s*\"([a-zA-Z0-9_\\-]+)\"");
      Matcher matcher = camundaVariableNamePattern.matcher(formText);
      while (matcher.find()) {
        formFields.add(matcher.group(1));
      }
      return formFields;
    } catch (IOException e) {
      throw new RuntimeException("Can't find form at " + formResourceLocation);
    }
  }

(I also cache the results as we don’t want to scan the form html for every test!)

So now our tests can verify that when they complete a task, all variables in the form must be set - which pretty well covers my needs:

  public T completeTaskWithVariables(Map<String, Object> variables) {
    Set<String> formFields = camundaTestService.getTaskFormFields(getCurrentTask());
    Set<String> currentVariables = processVariables().keySet();
    SetView<String> requiredFields = Sets.difference(formFields, currentVariables);
    assertThat(variables.keySet()).containsAll(requiredFields);
   camundaTestService.getTaskService().complete(getCurrentTask().getId(), variables);
   return self();
 }

(Note we are writing our own test bits with some stuff copied from camunda-bpm-assert as we can’t run that library directly due to AssertJ collisions with Spring Boot: https://github.com/camunda/camunda-bpm-assert/issues/90 )