Authored and maintained by Daniel Akiva and Derek Abdine (derek abdine gmail dot com).
The primary goal is to build a framework that focuses on quickly bringing test cases online without having to mess with boilerplate code for configuring a rules environment. We want to be able to write tests that are portable between rules engines so that migrating from one engine to another without breaking rules can be done with confidence.
Some rules engines have been integrated with Spring. A similar approach to what trools provides can be exposed by working with Spring's support for JSR-94 and specific rule engine implementations. Trools was designed to be spring-agnostic.
Extending trools to support other rules engines is a trivial task. Please submit a ticket if you'd like to see support for other JSR-94 rules engines. The currently supported engines are:
- Drools
- Jess
Below are a couple examples to help you get started. Keep in mind that the test logic has not changed between the jess/drools examples -- only the test class configuration (by way of annotations) and the rule language has changed. This helps facilitate test-driven development for rules.
Trools depends on JUnit 4.1+ and the JSR-94 specification, as well as a plugin JSR-94 implementation (aka rules engine). As of October 17th 2013, trools is an official Sonatype OSS project and is synchronized with the Maven central repository. The maven dependency configuration would look like:
<dependencies>
<dependency>
<groupId>com.rapid7.trools</groupId>
<artifactId>trools-core</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Create your rules file:
(defclass Person org.rapid7.trools.jess.example.fact.Person static)
(defrule simple-test
?personFact <- (Person
(name ?name &: (eq ?name "Bill")))
=>
(retract ?personFact)
(add (new String "hello world"))
)
Create a trools-powered stateless rule session test case:
@RunWith(JessRulesJUnit4ClassRunner.class)
@RuleContext(resourceURI = "org/rapid7/trools/jess/example/simple-test.clp")
public class StatelessExampleTest {
@Test
public void statelessExample() throws InvalidRuleSessionException,
RemoteException {
// when
List results = m_ruleSession.executeRules(Arrays.asList(new Person().setName("Bill")));
// then
assertEquals("hello world", results.get(0));
}
@InjectRuleSession
private StatelessRuleSession m_ruleSession;
}
OR create a trools-powered stateful rule session test case:
@RunWith(JessRulesJUnit4ClassRunner.class)
@RuleContext(resourceURI = "org/rapid7/trools/jess/example/simple-test.clp")
public class StatefulExampleTest {
@Test
public void statefulExample() throws InvalidRuleSessionException,
RemoteException {
// given
m_ruleSession.addObject(new Person().setName("Bill"));
// when
m_ruleSession.executeRules();
// then
List results = m_ruleSession.getObjects(new ClassTypeObjectFilter(
String.class));
assertEquals("hello world", results.get(0));
}
@InjectRuleSession
private StatefulRuleSession m_ruleSession;
}
Create your rules file:
package org.rapid7.trools.example;
import org.rapid7.trools.example.Person;
rule "simple-test"
when
p : Person ( name == "Bill" )
then
retract(p);
insert(new String("hello world"));
end
Create a trools-powered stateless rule session test case:
@RunWith(DroolsJUnit4ClassRunner.class)
@RuleContext(resourceURI = "org/rapid7/trools/drools/example/simple-test.drl")
public class StatelessExampleTest {
@Test
public void statelessExample() throws InvalidRuleSessionException,
RemoteException {
// when
List results = m_ruleSession.executeRules(Arrays.asList(new Person().setName("Bill")));
// then
assertEquals("hello world", results.get(0));
}
@InjectRuleSession
private StatelessRuleSession m_ruleSession;
}
OR create a trools-powered stateful rule session test case:
@RunWith(DroolsJUnit4ClassRunner.class)
@RuleContext(resourceURI = "org/rapid7/trools/drools/example/simple-test.drl")
public class StatefulExampleTest {
@Test
public void statefulExample() throws InvalidRuleSessionException,
RemoteException {
// given
m_ruleSession.addObject(new Person().setName("Bill"));
// when
m_ruleSession.executeRules();
// then
List results = m_ruleSession.getObjects(new ClassTypeObjectFilter(
String.class));
assertEquals("hello world", results.get(0));
}
@InjectRuleSession
private StatefulRuleSession m_ruleSession;
}
That's it. Run the test cases and you should see results :) For more interesting test cases, try combining mockito mock objects with trools test cases by passing the mock objects to the StatefulRuleSession#addObject(Object) or StatelessRuleSession#executeRules(List) methods, which can then be used by your rules and verified in the unit test methods.
We need support for other rules engines!
When making contributions, please use Java coding standards. If using Eclipse to develop changes, these standards are built in and can be turned on in the Eclipse project Java code style settings configuration page.
This project is available under the MIT license.