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.

Image for post
Image for post
Wildspitz Switzerland

So why should you bother testing them?

  1. It would blow up during Runtime. Who likes that?

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😀

  1. As a developer I want to test the DMNs that I get from the Business, even not knowing the concrete rules.

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

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:

Image for post
Image for post

Docker Setup

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.

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
Image for post
Image for post

As we have only specified one Path (/dmnConfigs), there is nothing to do here.

Image for post
Image for post

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.

Image for post
Image for post

We start with the ‘dish’ table:

  • First we need the decisionId:
Image for post
Image for post
  • The DMN Path is DMN’s file name plus the path where we put it.
Image for post
Image for post
  • 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.

Image for post
Image for post

This shows you that this DMN is correct:

Image for post
Image for post

Each Test Case is one row, that gives you the following information:

  • The test inputs the DMN table was evaluated with.

As everything worked — there is not much to do. Let’s add some Bugs😇.

Invalid Input

Image for post
Image for post

With these warnings you find the typo on first sight.

Image for post
Image for post

Here we missed the boundary case. Together with the valid tests for Spring we see that we used < 4 instead of ≤ 4.

Image for post
Image for post

Ok here now the test — do you see the problem😀:

Image for post
Image for post
Image for post
Image for post

Solution: we have 5 not included with (5..8] — we need [5..8].

Invalid Output

Image for post
Image for post

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.

Image for post
Image for post

You see that with a guest count of 5 we have 2 matches. The same result with the Hit Policy FIRST:

Image for post
Image for post

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:

Image for post
Image for post

Running the Tests again gives you now the following result:

Image for post
Image for post

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:

Image for post
Image for post

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.

Image for post
Image for post

Our DMN is empty of course. We define the first Rule in the DMN Config Editor:

Image for post
Image for post

As expected there is no Rule yet:

Image for post
Image for post

After adding the Rule:

Image for post
Image for post

The Rule matches and we can go to the next Rule.

Image for post
Image for post

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.

Try it out and let me know what you think!

Written by

Working for finnova.com in the Banking business. Prefer to work with Scala / ScalaJS.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store