How to write a unit test for a quite big bpm

Hi
I wrote a little unit test for a simple bpm before for learning about unit test .
And I succeeded with the guidance of friends here.
Now i would like write a new unitTest for a quite big bpm that has about 5 exclusive gateWay .
I got a bit confused .
How can i write a unit test for any condition based on different gateWay ?

please guide me .

You can write tests that have various conditional steps:

Look at the examples here:

These cover multiple examples where you can document your expected business flow in
business language" and then iterate through your needs. See the pictures in the coverage generation example for how to use “Data Tables” to iterate different pathways and your expected outputs for the input.

3 Likes

Hi @rezza72,

additionally to Stephens comment, you can start the unit tests at every task in your process: https://docs.camunda.org/manual/7.10/user-guide/process-engine/process-instance-modification/#process-instance-modification-in-junit-tests. No need to start each test at the start event.

This helps you to create small units and you don’t have to adapt all tests when you change your process model.

Together with the graphical test coverage, you will get a complete test quite quickly.

Hope this helps, Ingo

2 Likes

Hi
I think i didn’t say my purpose very well .

Before , i created a bpm file . see following picture :

1

And i wrote a test unit for bpm .
My test Code is :

import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.test.Deployment;
import org.camunda.bpm.engine.test.ProcessEngineRule;

import static org.assertj.core.api.Assertions.assertThat;
import static org.camunda.bpm.engine.test.assertions.ProcessEngineAssertions.assertThat;
import static org.camunda.bpm.engine.test.assertions.ProcessEngineTests.*;

import org.junit.Rule;
import org.junit.Test;

public class FinalTest {
	@Rule
	public ProcessEngineRule rule = new ProcessEngineRule();

	@Test
	@Deployment(resources = { "TestProcess.bpmn" })
	public void executeProcess() {
		ProcessInstance processInstance = runtimeService().startProcessInstanceByKey("TestProcess");
		// run the process
		assertThat(processInstance).isActive();
		// just one flow should be there
		assertThat(processInstanceQuery().count()).isEqualTo(1);

		// arrive to task1
		assertThat(processInstance).task("declaration");
		// task1 shouldn't be null
		assertThat(task(processInstance)).isNotNull();
		// task1 should be complete
		complete(task(processInstance));

		// arrive to task2
		assertThat(processInstance).task("confirmation");
		// task2 shouldn't be null
		assertThat(task(processInstance)).isNotNull();
		// task2 should be complete IF prc_ok == false
		complete(task(processInstance), withVariables("prc_ok", false));

		// arrive to task1
		assertThat(processInstance).task("declaration");
		// task1 shouldn't be null
		assertThat(task(processInstance)).isNotNull();
		// task1 should be complete
		complete(task(processInstance));

		// arrive to task2
		assertThat(processInstance).task("confirmation");
		// task2 shouldn't be null
		assertThat(task(processInstance)).isNotNull();
		// task2 should be complete IF prc_ok == true
		complete(task(processInstance), withVariables("prc_ok", true));

		// process should be end
		assertThat(processInstance).isEnded();

	}
}

in my code , i said if prc_ok == true , go to end event .
and if prc_ok == false , return to tasks1 .

as you see , this test unit is very simple .

now i create a new bpm file . see following picture :

if i start writing a unit test , i must write many test code for every simple of conditions .

i hope i could what i say .

Look at the links i posted above and specifically look at the use of the Data Tables examples. You can create a data table of paths that represent the various combinations of gateway routes. Then you build a single test with conditional sections that execute based on the T/F paths defined in the data table.

So you only need to write ~1 / a small number of tests and the rest of the variance can come from a data table structure that loops through the variance.

1 Like

Hi @rezza72,

although Stephens proposal looks promising, I would like to clarify my approach as well.

First, only test the happy path of the process from start to end.
Then, add all remaining sections and gateways in small units with the help of process instance modifications. The pattern for this is:

  • start process after one activity with all process variables
  • assert new state of the process.

Repeat this pattern until the graphical test coverage is 100 percent.

You can find an example here: https://github.com/ingorichtsmeier/show-case-for-tests/blob/master/example-testshowcase/src/test/java/com/camunda/consulting/example_testshowcase/InMemoryH2Test.java.

If you run the project with mvn clean test you can inspect the html files in target/process-test-coverage folder.

Don’t test obvious results from the process engine, as they blow up the test code without adding additional value.

I would like to test your complex process as well if you add the bpmn file here.

I’m too lazy to model this complex process…

Hope this helps, Ingo

1 Like

@Ingo_Richtsmeier in your example, wouldn’t this still mean you need to duplicate the steps to arrive at the exception paths? And there would often be multiple happy paths.

If you have a lot of parallel activity it gets more complicated, but this example is a set of XOR gateways, this you can just have a data table with 5 decision points/ columns, and you add your conditional testing sections based on those decision points. This way you can continually add new path ways with a minimal change to a conditional section.

1 Like

Hi
Thanks @StephenOTT and @Ingo_Richtsmeier for your clear guidance .
but i don’t know anything about table decision . :frowning_face: :roll_eyes:

@rezza72 you have to look at the code I linked through. Here is a specific example that uses data tables with if statements.

https://github.com/DigitalState/Camunda-Spock-Testing/blob/master/End-to-End/src/test/groovy/end-to-end/EndToEndSpec.groovy

1 Like

Hi @StephenOTT,

no, I didn’t duplicate the code.

As happy path I associate the most usual path taken from the start event to the end event. This is tested completely by stepping through the process with either complete(task(), withVariables(...)); or execute(job());.

The variables passed in completion serve as a specifcation for the form designer and delegate- or externa-task implementation, which variables the process expect to continue.

For humans you can describe the remaining tests as: Junp into the process at activity A with state X and execute the activity. Assert that the process has reached activity B with state Y.

So, all other units are started somewhere in the process with

    ProcessInstance processInstance = runtimeService()
        .createProcessInstanceByKey(PROCESS_DEFINITION_KEY)
        .startBeforeActivity("UserTask2Task")
        .execute();

Usually you will create the state of the process instance with .setVariables(withVariables(var1, value1, var2, value2, ...)).

The complete methods may look like this: https://github.com/ingorichtsmeier/show-case-for-tests/blob/master/example-testshowcase/src/test/java/com/camunda/consulting/example_testshowcase/InMemoryH2Test.java#L58-L70.

I will have a look at your data table approach.

Cheers, Ingo

2 Likes