Testing your Business Rules in JBoss Drools

Exclusive offer: get 50% off this eBook here
JBoss Drools Business Rules

JBoss Drools Business Rules — Save 50%

Capture, automate, and reuse your business processes in a clear English language that your computer can understand.

£18.99    £9.50
by Paul Browne | April 2009 | Java Open Source

This article by Paul Browne, shows how to test your business rules so that mistakes don't happen in real-time. It shows that testing is an ongoing process and begins the testing by using Guvnor. It then shows how to test rules against requirement documents using the FIT (Framework for Integrated Testing).

When we start writing 'real' business rules, there is very little space to make mistakes as the mistakes made can cause a lot of wastage of money. How much money would a company lose if a rule that you wrote gave double the intended discount to a customer? Or, what if your airline ticket pricing rule started giving away first class transatlantic flights for one cent?

Of course, mistakes happen. This article makes sure that these costly mistakes don't happen to you. If you're going through the trouble of writing business rules, you will want to make sure that they do what you intend them to, and keep on doing what you intend, even when you or other people make changes, both now and in the future. But first of all, we will see how testing is not a standalone activity, but part of an ongoing cycle.

Testing when building rules

It's a slightly morbid thought, but there's every chance that some of the business rules that you write will last longer than you do. Remember the millennium bug caused by programmers in the 1960's, assuming that nobody would be using their work in 40 years' time, and then being surprised when the year 2000 actually came along?

Rather than 'play and throw away', we're more likely to create production business rules in the following cycle:

  1. Write your rules (or modify an existing one) based on a specification, or feedback from end users.
  2. Test your rules to make sure that your new rules do what you want them to do, and ensure that you haven't inadvertently broken any existing rules.
  3. Deploy your rules to somewhere other than your local machine, where end users (perhaps via a web page or an enterprise system) can interact with them.

You can repeat steps 1, 2, and 3 as often as required. That means, repeat as many times as it takes you to get the first version into production. Or, deploy now and modify it anytime later –in 1, 2, or 10 years time.

Making testing interesting

Normal testing, where you inspect everything manually, is booooooooring! You might check everything the first time, but after the hundredth deployment you'll be tempted to skip your tests—and you'll probably get away with it without any problems. You'll then be tempted to skip your tests on the 101st deployment—still no problems. So, not testing becomes a bad habit either because you're bored, or because your boss fails to see the value of the tests.

The problem, then comes one Friday afternoon, or just when you're about to go on vacation, or some other worst possible time. The whole world will see any mistakes in the rules that are in production. Therefore, fixing them is a lot more time and money consuming than if you catch the error at the very start on your own PC.

What's the solution? Automate the testing. All of your manual checks are very repetitive—exactly the sort of thing that computers are good at. The sort of checks for our chocolate shipment example would be 'every time we have an order of 2000 candy bars, we should have 10 shipments of 210 bars and one shipment of 110 bars'.

Testing using Guvnor

There is one very important  advantage of testing—we can instantly see whether our tests are correct, without having to wait for our rules to be deployed into the target system.

At a high level, Guvnor has two main screens that deal with testing:

  • An individual test screen: Here you can edit your test by specifying the values that you want to input, and the values that you expect once your rules have fired
  • A package or multiple tests screen (below): This allows you to run (later on) all of the tests in your package, to catch any rules that you may have inadvertently broken

Another way of saying this is: You write your tests for selfish reasons because you need them to ensure that your rules do what you want them to do. By keeping your tests for later, they automatically become a free safety net that catches bugs as soon as you make a change.

JBoss Drools Business Rules

Testing using FIT

Guvnor testing is great. But, often, a lot of what you are testing for is already specified in the requirements documents for the system. With a bit of thought in specifying various scenarios in your requirements documents, FIT allows you to automatically compare these requirements against your rules. These requirements documents can be written in Microsoft Word, or similar format, and they will highlight if the outputs aren't what is specified. Like Drools, FIT is an open source project, so there is no charge for either using it, or for customizing it to fit your needs.

Before you get too excited about this, your requirements documents do have some compromises. The tests must specify the values to be input to the rules, and the expected result—similar to the examples, or scenarios, that many specifications already contain. These scenarios have to follow a FIT-specific format. Specification documents should follow a standard format anyway—the FIT scenario piece is often less than 10% of it, and it is still highly human-readable! Even better, the document can be written in anything that generates HTML, which includes Microsoft Word, Excel, OpenOffice, Google documents, and most of the myriad of editors that are available today.

Like the Guvnor testing, we can use FIT to test whether our individual requirements are being met when writing our rules. It is possible to run FIT automatically over multiple requirement documents to ensure that nothing has 'accidentally' broken as we update other rules.

Getting FIT

When you download the samples, you will probably notice three strange packages and folders.

  • fit-testcase: This folder resides just within the main project folder, and contains the FIT requirements documents that we're going to test against.
  • chap7: This is a folder under src/main/java/net/firstpartners, and contains the startpoint (FitRulesExample.java) that we'll use to kick-start our FIT Tests.
  • FIT: This folder is next to the chap7 folder. It folder contains some of the 'magic plumbing' that makes FIT work. Most business users won't care how this works (you probably won't need to change what you find here), but we will take a look at it in more detail in case we want to customize exactly how FIT works.

If you built the previous example using Maven, then all of the required FIT software will have been downloaded for you. (Isn't Maven great?) So, we're ready to go.

The FIT requirements document

Open the word document fit-testcase.doc using Word, or OpenOffice. Remember that it's in the fit-testcase folder. fit-testcase.doc is a normal document, without any hidden code. The testing magic lies in the way the document is laid out. More specifically, it's in the tables that you see in the document. All of the other text is optional. Let's go through it.

Logo and the first paragraph

At the very top of the document is the Drools logo and a reference to where you can download FIT for rules code. It's also worth reading the text here, as it's another explanation of what the FIT project does and how it works.

JBoss Drools Business Rules

None of this text matters, or rather FIT ignores it as it contains no tables. We can safely replace this (or any other text in this document that isn't in the table) with your company logo, or whatever you normally put at the top of your requirement documents.

FIT is a GPL (General Public License) open source software. This means you can modify it (as long as you publish your changes). In this sample we've modified it to accept global variables passed into the rules. (We will use this feature in step 3.)
The changes are published in the FIT plumbing directory, which is a part of the sample. Feel free to use it in your own projects.

First step—setup

The setup table prepares the ground for our test, and explains the objects that we want to use in our test. These objects are familiar as they are the Java facts that we've used in our rules. There's a bit of text (worth reading as it also explains what the table does), but FIT ignores it. The bit that it reads is given in the following table:

net.firstpartners.fit.fixture.Setup

net.firstpartners.chap6.domain.CustomerOrder

AcmeOrder

net.firstpartners.chap6.domain.OoompaLoompaDate

nextAvailableShipmentDate

If you're wondering what this does, try the following explanation in the same table format:

Use the piece of plumbing called 'Setup'

 

Create CustomerOrder and call it

AcmeOrder

Create OoompaLoompaDate and call it

nextAvailableShipmentDate

There is nothing here that we haven't seen before. Note that we will be passing nextShipmentDate as a global so that it matches the global of a same name in our rules file (the match includes the exact spelling, and the same lower-and uppercase).

Second step—values in

The second part also has the usual text explanation (ignored by FIT) and table (the important bit), which explains how to set the values.

net.firstpartners.fit.fixture.Populate

AcmeOrder

Set initial balance

2000

AcmeOrder

Set current balance

2000

It's a little bit clearer than the first table, but we'll explain it again anyway.

Use the piece of plumbing called Populate

AcmeOrder

Take the ... we created earlier, and set it to have an initial balance of ...

2000

AcmeOrder

Take the ... we created earlier, and set it to have a current balance of ...

2000

Third step—click on the Go button

Our next part starts the rules. Or rather, the table tells FIT to invoke the rules. The rest of the text (which is useful to explain what is going on to us humans) gets ignored.

net.firstpartners.fit.fixture.Engine

Ruleset

src/main/java/net/firstpartners/chap6/shipping-rules.drl

Assert

AcmeOrder

Global

nextAvailableShipmentDate

Execute

 

The following table is the same again, in English:

Use the piece of plumbing called 'Engine'

Ruleset

Use the rules in shipping-rules.drl

Assert

Pass our AcmeOrder to the rule engine (as a fact)

Global

Pass our nextAvailableShipmentDate to the rule engine (as a global)

Execute

Click on the Go Button

JBoss Drools Business Rules Capture, automate, and reuse your business processes in a clear English language that your computer can understand.
Published: April 2009
eBook Price: £18.99
Book Price: £30.99
See more
Select your format and quantity:

Fourth step—check the results

After running our rules, we check to see if the results are as we expected.

net.firstpartners.fit.fixture.Results

AcmeOrder

Get current balance

0

AcmeOrder

Get initial balance

2000

The following table explains whether the results are as we expected:

Use the piece of plumbing called 'Results'

AcmeOrder

Check the ... we created earlier, and make sure we now have a current balance of ...

0

AcmeOrder

Check the ... we created earlier, and make sure we now have the initial balance of ...

2000

Clear (an optional step)

This optional step clears any of the previous steps. This means that, if we want, we can repeat steps 1 to 4 again in the same requirements document.

net.firstpartners.fit.fixture Clear

Print a summary (an optional step)

Another optional step is to print a summary of how our tests went. This is in addition to the information that will append to each of the previous steps.

fit.Summary

Footer

Finally, there is some more narrative in the footer. FIT ignores this, so you can remove or replace this if you want.

There is an important part in this footer. It's a note that the sample (that we're now using) is based on FIT, and also on a sample by Michael Neale from the Drools team—http://fit-for-rules.sourceforge.net/. This sample is based on GPL licensed code. My understanding (remember that I'm not a lawyer) is that you can use it internally within your organization; but if it is used outside, then you will need to publish any changes (if you made any) to the core FIT testing code.

Running FIT on our sample

So far, all we've seen is a Word document. Let's make the magic happen by running FIT against this document. In this example we're running FIT through the JBoss IDE, but it would be easy for anyone with a moderate knowledge of Java (ask your friendly technical person) to make this work by double-clicking on a Windows icon. The steps for running FIT on our sample are given as follows:

  1. FIT doesn't run against the Word document, but against the HTML version of the Word document. Save your Word document as HTML. (In Word, choose File | Save As | Web Page) Name the file as fit-testcase.htm
  2. Run the FIT test. In this case select FitRulesExample in the JBoss IDE (package explorer or navigator), and right-click on it. Select Run As | Java Application from the context menu that is dispalyed.
  3. You should see a bunch of stuff in the console, beginning with net firstpartners.chap7.FitRulesExample main and ending with Clearing domain objects.
  4. In between, you'll see the results of our rules firing, such as 28 right, 0 wrong, 23 ignored, 0 exceptions. Remember that we automate our testing in order to automate our inspection of the results. So don't bother reading the console, other than to check that FIT has run successfully.
  5. To see the results, open fit-test-result.htm. The filenames that we use (fit-testcase.htm and fit-test-result.htm) are set in the FitRulesExample.java file, and can be changed to whatever you want.

 

What just happened?

Fit-test-result.htm is a copy of the original FIT test case, but it has been updated with the results of our tests. If everything goes well, all of our tables should be highlighted. As an example, the fourth table where we check the results is shown as follows:

net.firstpartners.fit.fixture.Results

AcmeOrder

Get current balance

0

AcmeOrder

Get initial balance

2000

The other main change has been our (optional) summary table. FIT has updated the output with a summary of the set of tests that it has just run. Most importantly, it's highlighted.

fit.Summary

 

counts

28 right, 0 wrong, 23 ignored, 0 exceptions

input file

C:projects-droolschap6-samplefit-testcasesfit-testcase.htm

input update

Thu Sep 04 22:31:39 BST 2008

output file

C:projects-droolschap6-samplefit-testcasesfit-test-result.htm

run date

Thu Sep 04 22:43:44 BST 2008

run elapsed time

0:01.58

There is a small bug in the version of FIT used here (we didn't have 28 tests). But a test that fails will always be picked up, which leads us to the question, "What does a failed test look like?"

What can go wrong?

Let's imagine that our requirements document was slightly different—it said that our rules have run, and so we should still have a balance left on our order of 150 bars still to ship. In other words, table 4 of our requirements document should look something like this:

net.firstpartners.fit.fixture.Results

AcmeOrder

Get current balance

150

AcmeOrder

Get initial balance

2000

When we save the document using Save as | HTML and run our FIT tests again (using the steps above), we'll notice two differences. The first indication that a test has failed is in the console of the JBoss IDE, where we'll see the message: 25 right, 3 wrong, 23 ignored, 0 exceptions. The second change is in the FIT document, where the results table (table 4) is highlighted in dark gray. Helpfully, it shows you the actual and expected values. The following figure shows you a failing FIT test:

Net.firstpartners.fit.fixture.Results

AcmeOrder

Get current balance

150 expected


0 actual

AcmeOrder

Get initial balance

2000

Normally, we would revise our rules and run our FIT tests again until our requirements were met (highlighted).

That was a failing rule. But what if something more serious happens that causes our rules to 'blow up', or throw an exception? We made a note while talking about the rules that it was important that the rule matches the name of our global variable.

What happens if it doesn't? Let's change the name of the variable in tables 1 and 3. The following table shows the setup using some made-up name:

net.firstpartners.fit.fixture.Setup

net.firstpartners.chap6.domain.CustomerOrder

AcmeOrder

net.firstpartners.chap6.domain.OoompaLoompaDate

someMadeupName

The following table shows how to execute the rule using some made-up name:

net.firstpartners.fit.fixture.Engine

Ruleset

src/main/java/net/firstpartners/chap6/shipping-rules.drl

Assert

AcmeOrder

Global

someMadeupName

Execute

 

When we save the document using Save as | HTML and we run FIT again, we see an indication of problems in the console (22 right, 3 wrong, 23 ignored, 2 exceptions) with full details of the error in the updated FIT results document. It is shown as follows with some details removed, and others highlighted for clarity:

org.drools.spi.ConsequenceException: java.lang.NullPointerException
...
Caused by: java.lang.NullPointerException
at net.firstpartners.chap6.Rule_Add_Next_Available_Shipment_Date_
0.consequence(Rule_Add_Next_Available_Shipment_Date_0.java:10)
... 19 more

When you first see this, there is too much detail (about 19 lines of technical-looking text, in this example), but the important bits are:

  • The first line that tells us we have a nullpointer exception. This is where a rule expects to have something available; but what it expects isn't there.
  • The Caused by line indicates that the problem is in Rule_Add_Next_Available_Shipment, and more particularly in the consequence (the 'when' part).

Looking at the When part of Rule_Add_Next_Available_Shipment,we see that it contains the code nextAvailableShipmentDate.rollForward(7).This assumes that our nextAvailableShipmentDate has something passed in to it. But it doesn't, as we've just changed the FIT rules to use a different name. Hence, it is empty, and causes the NullPointerException that we see.

The FIT plumbing

If you're interested only in testing against requirements, then you should presume that FIT for rules 'just works' and skip ahead to the next section (on unit testing). If you're somewhat technical, you're probably wondering how FIT works under the covers. If so, read on.

In general, the classes that were mentioned in the FIT template (for example, net.firstpartners.fit.fixture.Setup) are the plumbing that allows the FIT framework to understand our rules code. These files can be found in the src/main/java/net/firstpartners/fit folder.

The most likely scenario is that 99% of what you need is already covered by this (modified) FIT for Rules framework, but you may come across something that you would like to add, or do differently. For example, the modified framework can pass global variables to the rules, whereas the original source cannot.

The full source is available for both, so these are just pointers to get you started:

  1. When we start FitRulesExample, Java finds our main() method and jumps in. This method sets up parameters (such as input and output files) and calls the FileRunner file (run method).
  2. FileRunner is a part of the FIT framework—the source code is available. But as a summary, it loads the input document that we specified and scans it for tables that may contain instructions to carry out a test.
  3. You'll notice that the first line of all of the tables in our FIT document start with an instruction—Setup, Populate, Engine, Results, Clear, and Summary. When the FIT framework finds one of these, it tries to find the file, and loads it.
  4. All of these instruction files follow a similar format. They 'extend' another file called AbstractRulesTesting, which is like a template. So when we are reading a file (for example, Setup.java), we must remember that it also contains all of the code from AbstractRulesTesting. (Because many files use this common template, it saves us a lot of typing.)
  5. At this point, FIT has scanned the document, found the table, and loaded the instruction file (for example, Setup.java). It loops through each cell in the table and calls the doCell() method in the Setup.java (or other instruction) file.
  6. The doCell() method checks to see what sort of cell it is. It may carry out different actions depending on whether it's the first row, the first column, or any other cell.

In this way, FIT loops through the document and sets up the facts to be tested, calls the rule engine, checks the results, and then prints a summary.

Remember that we would normally write the instruction file to understand our code. It just happens that we have a set of adaptors (in FIT for Rules) that understands most of the rules-related code. If we need to modify an adaptor, we can add pretty much any Java-based code that's required.

Summary

In this article we've seen how to test our rules using Guvnor, as well as using FIT for rule testing against requirements documents. The actual mechanics of calling the rules from FIT are the same as our previous examples. What is different in FIT for Rules is that we express our tests in Word, and (via our adaptors) FIT understands what values to pass to the rules.

 

If you have read this article you may be interested to view :

 

JBoss Drools Business Rules Capture, automate, and reuse your business processes in a clear English language that your computer can understand.
Published: April 2009
eBook Price: £18.99
Book Price: £30.99
See more
Select your format and quantity:

About the Author :


Paul Browne

Paul Browne's first job was selling computers in France and things went steadily downhill from there. He spent millons on behalf of a UK telephone company's procurement department and implemented direct marketing for a well-known Texan computer maker before joining the IT department of a company that builds bright red tractors and other seriously cool machines.

Paul then embraced his techie side (he was writing games in machine code from the age of 11) and started a consultancy that used IT to solve business problems for companies in the financial and public sectors in Ireland, UK , Belgium, and New Zealand. Eight years later, he now works with an Irish government agency that helps similar software companies to grow past their initial teething pains.

More formally, Paul has a bachelor's degree in Business and French from the University of Ulster, a master's degree in Advanced Software from UCD Dublin, a post-grad qualification in Procurement from the Chartered Institute of Procurement and Supply (UK), and will someday complete his ACCA financial exams.

Paul can be found on LinkedIn at http://www.linkedin.com/in/paulbrowne , and via the Red Piranha (Business knowledge) project at http://code.google.com/p/red-piranha/ .

Books From Packt

 

JBoss Portal Server Development
JBoss Portal Server Development

Learning jQuery 1.3
Learning jQuery 1.3

DWR Java AJAX Applications
DWR Java AJAX Applications

JBoss Tools 3 Developers Guide
JBoss Tools 3 Developers Guide

Java EE 5 Development with NetBeans 6
Java EE 5 Development with NetBeans 6

Spring 2.5 Aspect Oriented Programming
Spring 2.5 Aspect Oriented Programming

Drupal 6 Social Networking
Drupal 6 Social Networking

WordPress Plugin Development: Beginner's Guide
WordPress Plugin Development: Beginner's Guide

 

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software