Testing (Camunda)-DMN Tables automatically
Working with DMN Tables is quite easy. It is like working with an Excel Spread Sheet. The idea is that the business people define the Business Rules with these tables.
So why should you bother testing them?
- It would blow up during Runtime. Who likes that?
- Depending on the Hit Policy the Result is different.
- How can you be sure you covered all possible cases?
- You have a second gate when making small adjustments.
- You have Regression Tests in place in case of updates.
- You get a nice documentation of the possible inputs and its outputs.
To convince business people to start working Test-Driven, looks to me not very promising.
So let’s build a Tool that makes testing DMN tables a breeze😀
- As a developer I want to test the DMNs that I get from the Business, even not knowing the concrete rules.
- Business people can create their own tests.
- They can easily adjust the tests to the dynamic nature of DMN Tables.
And this is what this blog is all about. How can you use this DMN Tester that tries to adhere to these three goals.
Requirements
You can use the Tester as an JAVA-Application or in a Docker Container.
At the moment I only tested the tool on Mac. So the steps needed are the ones for Mac Users. However in the end we start a standard JAVA application that should also run on Windows.
See the chapter Docker Setup if you want to use the Tester in a Docker Container.
Install Ammonite
Update 22.2.2021: I removed the Ammonite Script for the Demo Project — so please use the Docker Setup to use the DMN Tester.
Ammonite we use to easily install, configure and start the DMN Tester.
$ brew install ammonite-repl
For more information — see Ammonite.io
Get the Start Script
I prepared a directory, where you find everything that you need to follow this Blog:
Configure the Tester
Open testRunner.sc. The only thing you need to adjust is the config Paths. This is a comma separated list with Paths where you have your DMN Tester Configs.
If you follow this example you do not need to change anything!
Here is the example if you will have your Configs in dmnConfigs:
#!/usr/bin/env amm
// repo for DMN Tester
interp.repositories() ++= Seq( coursierapi.MavenRepository.of("https://dl.bintray.com/pme123/maven"))
// this compiles the script in 2 parts (add first resolver and then runs the script)
@
import $ivy.`pme123::camunda-dmn-tester-server:0.6.0`
import pme123.camunda.dmn.tester.server.HttpServer
// add here your comma separated list with Paths you have your DMN Tester Configs
val configPaths = "/dmnConfigs"
sys.props.addOne("TESTER_CONFIG_PATHS", configPaths)
HttpServer.main(Array.empty[String])
Start the Server
In the Directory of the testRunner.sc, you can start now the Server:
$ amm testRunner.sc
Here the console:
nbszmbp013:demo mpa$ amm testRunner.sc
Compiling /Users/mpa/dev/Github/pme123/camunda-dmn-tester/demo/testRunner.sc
Compiling /Users/mpa/dev/Github/pme123/camunda-dmn-tester/demo/testRunner.sc #2
Server starting at Port 8883
[ioapp-compute-0] INFO org.http4s.blaze.channel.nio1.NIO1SocketServerGroup - Service bound to address /0:0:0:0:0:0:0:0:8883
[ioapp-compute-0] INFO org.http4s.server.blaze.BlazeServerBuilder -
_ _ _ _ _
| |_| |_| |_ _ __| | | ___
| ' \ _| _| '_ \_ _(_-<
|_||_\__|\__| .__/ |_|/__/
|_|
[ioapp-compute-0] INFO org.http4s.server.blaze.BlazeServerBuilder - http4s v0.21.12 on blaze v0.14.14 started at http://[::]:8883/
Now open http://localhost:8883 in your Browser.
You should see something like this:
Docker Setup
You can run a Docker Container or use the image with Docker-Compose.
Docker-Compose:
You find a docker-compose.yml
in the demo
directory.
camunda-dmn-tester:
image: pame/camunda-dmn-tester:latest
environment:
TESTER_CONFIG_PATHS: "/dmnConfigs"
volumes:
- ./dmns:/opt/docker/dmns
- ./dmnConfigs:/opt/docker/dmnConfigs
ports:
- "8883:8883"
The volumes and the environment is analog as described above.
In the demo
directory run:
docker-compose -f docker-compose.yml --project-directory . -p camunda-dmn-tester up
Now open http://localhost:8883 in your Browser.
Docker Run:
Or just start a Docker Container with the following command:
docker run \
--name camunda-dmn-tester \
--rm \
-it \
-e TESTER_CONFIG_PATHS="/dmnConfigs" \
-v $(pwd)/dmns:/opt/docker/dmns \
-v $(pwd)/dmnConfigs:/opt/docker/dmnConfigs \
-p 8883:8883 \
pame/camunda-dmn-tester
Ok, line by line:
--name camunda-dmn-tester
Gives a readable name for the container:--rm
removes the container after finishing the tests.-it
is optional — the interactive mode gives me nice colors on my mac.-e TESTER_CONFIG_PATH="/dmnConfigs"
a comma-separated list where you have the DMN configurations.-v $(pwd)/dmnConfigs:/opt/docker/dmnConfigs
adds the DMN configurations to the container.-v $(pwd)/dmns:/opt/docker/dmns
adds the DMNs to the container.-p 8883:8883
the port of the DMN Tester App.pame/camunda-dmn-tester
is the image you need.
Camunda Modeler
To edit the DMNs I use the Camunda Modeler.
Let’s Test DMN Tables
We take the DMN from the Decision Requirements Graph (DRG) Example.
So we start with the following file structure:
|-testRunner.sc
|-dmns
|-dinnerDecisions.dmn
|-dmnConfigs
As we have only specified one Path (/dmnConfigs), there is nothing to do here.
If you get an Exception here, make sure you have created the Path (/dmnConfigs).
Hit the blue Button and you get a form, where you can configure the DMN Table.
We start with the ‘dish’ table:
- First we need the decisionId:
- The DMN Path is DMN’s file name plus the path where we put it.
- To figure out the inputs variables is a bit more tricky. Open the properties of an input. In this case it is simple — as the expression is the variable season.
- Checking the possible inputs, all we need now is to add them as a comma separated list, like Fall, Winter, Spring, Summer.
That’s it — hit the Save Button!
To run the configuration select it and Run it.
This shows you that this DMN is correct:
Each Test Case is one row, that gives you the following information:
- The test inputs the DMN table was evaluated with.
- The DMN Row that matched for these inputs (this row number you find in the Camunda Modeler).
- The Inputs that matched (these inputs you find in the Camunda Modeler).
- The Outputs that matched (these outputs you find in the Camunda Modeler).
As everything worked — there is not much to do. Let’s add some Bugs😇.
Invalid Input
With these warnings you find the typo on first sight.
Here we missed the boundary case. Together with the valid tests for Spring we see that we used < 4 instead of ≤ 4.
Ok here now the test — do you see the problem😀:
Solution: we have 5 not included with (5..8] — we need [5..8].
Invalid Output
This one is a bit trickier, but you will figure out that the Apostrophes (“) are missing.
Invalid Match Results
If you use the Hit Policy UNIQUE the DMN Tester can help you even better.
You see that with a guest count of 5 we have 2 matches. The same result with the Hit Policy FIRST:
So hard to say if this is what you wanted.
Test Case Creation
As you can imagine, the number of Test Cases can be overwhelming. So what we can do is to mark every Result that is correct and persist it to the DMN Configuration.
Just check the validated (correct) Rows and hit the blue button:
Running the Tests again gives you now the following result:
The DMN Row and the outputs are now checked if they haven’t changed. So if we change our DMN that changes the Result we see this right away:
Test Driven Development
We covered so far more or less the Use Cases where the Process Responsible gets the DMN and checks if it is valid.
In the beginning I even made this statement:
To convince business people to start working Test-Driven, looks to me not very promising.
So let’s see how the Business People can even use the DMN Tester to develop their DMNs in a Test Driven way.
Our DMN is empty of course. We define the first Rule in the DMN Config Editor:
As expected there is no Rule yet:
After adding the Rule:
The Rule matches and we can go to the next Rule.
Voilà — we can develop our DMNs step by step.
Open Issues
- After some changes in the DMN, the Cells (row-span) are not properly rendered — running the Tester again solves this.
Conclusion
The DMN Table Tester should give you:
- Validate a DMN efficiently.
- Develop your DMN ‘Test-Driven’.
- Add regression Tests on the fly.
- A tool you can give your Business People or at least communicate with them (explain problems).
Try it out and let me know what you think!
Update 22.2.2021: I added a Part 2 that show how to create Regression Tests with the DMN Tester.