maxheapsize.com test it. code it. ship it.

Test JBoss Rules 5 (or Drools) With TestNG

Posted on | Comments

We have been using our own flavor of Fit for Rules (which is build on top of fit) for about 1 1/2 years now to test our business logic written in JBoss Rules 5. It’s relatively easy to get the Business Analyst on board since he is using his tool (which is Microsoft Excel) to communicate test cases for the rules. So in theory, he writes the tests in Excel, we do the rules coding and voila, all tests turn green.

Reality is, we have to tweak the Excel sheets. We need to put in imports of our fact model, insert facts and create objects within that not so programmer friendly table environment. A couple of days ago we got the request to tweak some rules and we all had to start doing rules again (and we used to use Eclipse for writing rules because that’s the only IDE having a plugin for that).

After half a day of coding Java syntax in Excel sheets we decided that the ramp up time for the not so knowledgeable rules/fit programmers like me is too much. With debugging, copy and paste we spent easily 5-10 times more time on making the tests work than writing the code itself. Test driven design is not really an option here, since you need to know the imports of the rules file to get the sheet even to compile.

So what did we do ? Well why not try to get things working the way we used to do it ? TestNG anyone ?

There are many pros to use unit testing but also some cons. The biggest issue is that we will loose the direct communication to the business analyst. It’s always better if someone else writes the test and I just have to implement the solution. Maybe we can find another solution involving Active Spec or DSL. For now we stick to unit tests and the task the we have to make sure we convert every Excel test case to java code (but hey, that’s what our code reviews are for).

Checkout our current base class for testing our rules:

AbstractRulesTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public abstract class AbstractRulesTest {
   public abstract String[] getRulesFileNames();
   private final String GET_FINDINGS = "import com.maxheapsize.RulesFinding;" +
                                                                           "query \"getAllRulesFindings\"\n" +
                                                                           "   finding : FRulesFinding()\n" +
                                                                           "end";

   private static Logger LOG = Logger.getLogger(AbstractRulesTest.class);

   public final List<FRulesFinding> fireRules(Set factsForWorkingMemory) {
       KnowledgeBase ruleBase = setUpKnowledgeBase();
       return fireRules(ruleBase, factsForWorkingMemory);
   }

   public KnowledgeBase setUpKnowledgeBase() {
       KnowledgeBaseConfiguration configuration = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
       KnowledgeBase ruleBase = KnowledgeBaseFactory.newKnowledgeBase(configuration);

       KnowledgeBuilder build = KnowledgeBuilderFactory.newKnowledgeBuilder();
       build.add(ResourceFactory.newReaderResource(new StringReader(GET_FINDINGS)), ResourceType.DRL);
       String[] fileNames = getRulesFileNames();
       for (String fileName : fileNames) {
           File userDefinedFile = new File(fileName);
           build.add(ResourceFactory.newFileResource(userDefinedFile), ResourceType.DRL);
       }

       handleBuilderErrors(build);

       ruleBase.addKnowledgePackages(build.getKnowledgePackages());
       return ruleBase;
   }

   private void handleBuilderErrors(KnowledgeBuilder build) {
       if (build.hasErrors()) {
           KnowledgeBuilderErrors knowledgeBuilderErrors = build.getErrors();
           for (KnowledgeBuilderError knowledgeBuilderError : knowledgeBuilderErrors) {
               int[] ints = knowledgeBuilderError.getErrorLines();
               LOG.error("Error at : "+ints[0]+" : "+ints[1]);
               LOG.error(knowledgeBuilderError.getMessage());
           }
       }
   }

   private List<FRulesFinding> fireRules(KnowledgeBase ruleBase, Set facts) {
       List<FRulesFinding> result = new ArrayList<FRulesFinding>();
       StatefulKnowledgeSession statefulSession = ruleBase.newStatefulKnowledgeSession();
       for (Object fact : facts) {
           statefulSession.insert(fact);
       }
       statefulSession.fireAllRules();

       QueryResults results = statefulSession.getQueryResults("getAllRulesFindings");
       try {
           FRulesFinding finding = (FRulesFinding) results.iterator().next().get("finding");
           result.add(finding);
       }
       catch (NoSuchElementException e) {
           result = new ArrayList<FRulesFinding>();
       }
      return result;
   }
}

All my rules insert a RulesFinding (and only one at the moment) into the working memory when triggered. The rest is pretty easy. You subclass it, overwrite getRulesFileNames and call fireRules with a set of objects (your tests) which need be insert into the working memory. To get the finding back you need to execute an already inserted query which needs to have an identifier (line 3, 20, 52, 54). It will contain the result of your rule execution.

Sample code would look like this:

RulesTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class RulesTest extends AbstractRulesTest {

   private Set facts;

   @BeforeMethod
   public void setUp() {
       facts = new HashSet();
   }

   @Override
   public String[] getRulesFileNames() {
       return new String[]{
               "src/main/rules/myrules.drl",
               "src/main/rules/generealRules.drl"
           };
  }

   @Test
   public void testDemoRule() {

       FMyFact myFact = new FMyFact();
    myFact.setColor("green");
       facts.add(myFact); // add all your facts here

       List<FRulesFinding> findings = fireRules(facts);
       Assert.assertTrue(findings.size() == 1);
       FRulesFinding finding = findings.get(0);
       Assert.assertTrue(finding.getStatus() == FStatus.OK);
   }
 }

Depending on how you cut your rules you can extract the assertion of the status.

Each test case in Excel now takes about 5-10 lines of java code. Considering we are covering each rule with about 5-15 test cases and boundary conditions this amounts to 75-150 lines of test code. I take that any day over programming in Excel.

How Pair Programming Can Help You to Get Into Test Driven Development

Posted on | Comments

At my current job management buys into Test driven development. It slowly starts to become our primary development style.

Some advantages of TDD for me are:

  • Focus on problem solution
  • No unnecessary development for use cases dreamed up by the developer
  • Always make sure your code fulfills the requirements (tests)
  • If you have tests you can safely refactor your code at any time

Even though we all knew about the advantages of TDD, we figured out that we do not always write tests first. While this might me acceptable in some situations like UI tests, most of the time it is not desired. After seeing that problem for a couple of weeks (and always wondering how I did not write test first) all of the sudden I realized why sometimes it was easier to develop code with writing tests first and sometimes I just simply did not thought about it and had to write the tests later.

It’s simply because of pair programming.

What certainly happens to me when coding alone, is that sometimes I get into ‘crunch’ mode too fast. With a second person to slow me down, we are discussing the problem and make sure we both have the same understanding of what we are trying to achieve. During this process it is much clearer what needs to be done and what the goals are. This is a very important step. If I’m all by myself, I start writing down some classes, look at methods and I’m already thinking in code and not about the problem. Once you know what the real problem is, it is very easy to start writing your tests first. It helped me a lot.

Where pairing also helps is to focus both programmers to stay in TDD mode. Once the driver of the pair programming couple starts to hack away code, the other will remind him to write the tests first. This is a very valuable.

After a while I got better at test driven development, even programming alone.

So if you recognize that you do not write tests first (but you want to) and you yet again don’t know how that happened, try pair programming. You learn how to start with TDD (and get used to it) and get all the benefits of pair programming as well.

4 Ways to Test Your Code

Posted on | Comments

Whenever you write some code you better make sure you have it covered by some tests. There are different possibilities on how to achieve this (and you might want to use all of them).

Unit Tests

Unit tests are a classic. You usually write them while you code. Write tests, run failing test, write code and run passing test. Repeat. Keep this small and simple. Test the smallest unit which makes sense. You might end up with hundreds if not thousands of them. Make sure you have no external dependencies in them, like databases or external webservices. Mock them away. They are usually written in TestNG or JUnit. These tests can run at every check in in the VCS.

Integration Tests

Integrations Test are one step higher. You want to make sure your applications runs with all or some dependencies and things plays well together. These usually have a much higher run time since they might involve some external services. Usually you would use a framework like TestNG, nUnit for them. These tests are run not so often since they can be very expensive.

UI Tests

Many applications have some sort of graphical user interface. To make sure everything works alright there are several tools like Selenium or HP Quality Center to simulate a user going through the GUI. These tests are written more towards the end of the projects since in the beginning the interface might change fast and often and the overhead to change them might be significant. You usually can not run through all paths of your applications. Choose the ones which are the most critical. They can be run a couple of times a day depending on the size of the application and the tool (e.g. Quality Center tends to be sort of slow) you use.

Reference System Tests

If you have to implement some business rules or calculations and your client can specify these in Excel you might want to use a framework like Fit (or Fitnesse). You run your system against the values in the sheets and verify that it meets the expectation.

There are many more test possibilities and techniques to make sure the system meets the demands of the customer. These are just the ones I tend to use in my projects and I found them very useful. Tests (and specifications) with tools like Concordion or the use of a Behavior Driven Design frameworks like EasyB are offering nice ways to make sure the customers gets what he pays for (and he understands it). I still need to play with them.

How to Get Your Developers to Pay More Attention to the Burn Down Chart (and Have Fun)

Posted on | Comments

For the last couple of month our scrum master always drew our daily burn down chart. Nice and fine and it always happened automagically. It came to the point that we (the developers) only occasionally paid attention to it (a little less then we should). One day our scrum master stopped doing it and forced us to do by ourselves. While it seemed a bit strange at first it turns out it was a brilliant idea.

We are now reminded every day how much hours we burned (in contrast to we just complete cards but nobody calculates the sum and tells that everybody). If the result is far below the theoretical number of developers x 8 hours a day we quickly analyze the problem and try to solve it. We are more aware that we might be in trouble. This was not so much the case in the last sprints.

Self made burn down chart

It could be that it just works because it is new but I think it is worth a short. I certainly like how everybody stands around the burn down chart and tries to do mental arithmetic.

Two Upgrades to Cut Down Development Time

Posted on | Comments

No matter how good a developer is, with two hardware upgrades you can speed up most machines and cut down development time for everybody.

Your big fat IDE

Depending what you use to crunch out your code, these things are eating your memory. And then you have your application server running, and an internet browser with 20+ sessions, plus Twitter, plus Photoshop, plus, plus, plus.

I used to have 2 GB RAM in my laptop and I thought it is ok. Turns out, having 4 GB, speeds up my overall development significantly. No more swapping of programs and more programs can stay open. So far I never reached the point where my memory was exhausted (well I never really tried).

Swapping in 4 GB of RAM (DDR2) on my laptop took about 10 minutes and 50 USD. Really worth the money.

Your mama’s harddrive

My IDE of choice starts indexing all my files, scans them, creates indices (which will get updated every so often through maven) and so on. My applications server writes files, scans files for changes, deployed them and does thousand other things. This is your hard drive at work. Looking for a better solution I was looking at RAM disks to have the indices written on there but there is a more obvious solution. A Solid State Drive. I bought the OCZ Vertex 120 GB off Amazon. Putting this baby into a laptop can be a bit tricky (try OWC for MacBook install videos) but is certainly doable.

I ran some benchmark before and after to see the read/write speed up. The HD was a 7200 rpm drive and all values are in MB/sec.

OperationHDSSDfactor
Sequential Uncached Write (4k Blocks)53.09115.762.18
Sequential Uncached Write (256k Blocks)48.2975.531.56
Sequential Uncached Read (4k Blocks)11.4618.341.6
Sequential Uncached Read (256k Blocks)42.53115.392.71
Random Uncached Write (4k Blocks)1.0110.059.95
Random Uncached Write (256k Blocks)20.6779.033.82
Random Uncached Read (4k Blocks)0.2213.1959.95
Random Uncached Read (256k Blocks)7.67104.7413.65

I used xBench to get the read/write numbers. My laptop went up from a 118 to 194 in this benchmark. Especially the random read is where the SSD really shines.

The SSD was about 330 USD and it took me about 40 minutes from shutdown with the old hard drive to boot up with my SSD drive. Of course reinstalling your OS (or rolling it back from a backup) will take some time too.

Upgrading your processor is also an option but I never used that one on my Desktop machines or Laptops. If this ever was a bottle neck it usually meant that the whole machine was outdated (mainboard, bus, ram speed). But is an option as well.

I you have an older machine or even with a fast machine and you have some money left, I think upgrading these two components really makes your machine fly.

500 USD compared to the time you save on development is about nothing. So every employer should upgrade their developers machines with RAM and SSD’s. It pays out very soon (and you will make your developers happy, which is also important ;-) ).

Getting Started With JSF 2 (and Maven)

Posted on | Comments

I use Apache Trinidad at work and since JSF 2 is now final I decided to play with it a bit.

Of course this is going to be the classic Hello World example (as there are many other like this around the web).

First you need to setup a simple Maven project.

The file layout should be something like that.

Directory Layout for JSF 2 Ajax Demo

After that change your pom.xml to include the servlet and jsf2 libs.

pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.maxheapsize</groupId>
  <artifactId>jsf2demo</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>jsf2demo Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.0.0-b13</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.0.0-b13</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
    </dependency>

  </dependencies>
  <build>
    <finalName>jsf2demo</finalName>
  </build>

  <repositories>
    <repository>
      <id>maven2-repository.dev.java.net</id>
      <name>Java.net Repository for Maven</name>
      <url>http://download.java.net/maven/2</url>
    </repository>
  </repositories>

</project>

First we are going to create the managed bean to hold the name we are going to enter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.maxheapsize.jsf2demo;

import javax.faces.bean.*;
import java.io.Serializable;

@ManagedBean
@SessionScoped
public class HelloWorldBean implements Serializable {

  private String name = "";

  @ManagedProperty(value = "#{demoService}")
  private Service service;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public void setService(Service service) {
    this.service = service;
  }

  public String getReverseName() {
    return service.reverse(name);
  }
}

To demonstrate the dependency injection with JSF 2 I use a service to reverse the name. Simply add the @ManagedProperty with the name of the service and it will get injected. Of course we need the service. Here it is:

1
2
3
4
5
6
7
8
9
10
11
12
package com.maxheapsize.jsf2demo;

import javax.faces.bean.*;

@ManagedBean(name="demoService")
@ApplicationScoped
public class Service {

  public String reverse(String name) {
    return new StringBuffer(name).reverse().toString().toLowerCase();
  }
}

Very simple…isn’t it ?

The only thing what’s missing is the web.xml and the webpage itself.

The web.xml is pretty straight forward and should not show any surprises.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Web Application</display-name>

  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>

  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>
</web-app>

The index.xhtml has some differences from earlier versions of JSF. First, JSF now supports Ajax out of the box. To have this enabled you need to load the jsf.js javascript library. The inputText tag binds to our managed bean and includes an f:ajax tag. This tag determines which part should be rerendered (in this case the element with the id reverseName). F:ajax will execute on value holding components always with an ‘onchange’ event and on action components (like commandButtons) with an ‘onclick’ event. The output will trigger the injected service in our managed bean and return the reversed input string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html">

<h:head>
  <title>JSF Demo</title>
</h:head>
<h:body>
  <h:outputScript name="jsf.js" library="javax.faces" target="body">
</h:outputScript>
  <h1>Ajax JSF 2 Demo</h1>
  <h:form>

    <h:inputText id="name" value="#{helloWorldBean.name}">
      <f:ajax render="reverseName"/>
    </h:inputText>

    <h:commandButton value="Say reverse Hi via Ajax">
      <f:ajax execute="name" render="reverseName"/>
    </h:commandButton>
    <h:outputText id="reverseName" value="#{helloWorldBean.reverseName}"/>
  </h:form>

</h:body>
</html>

JSF 2 Ajax Demo

And that’s it. Sure it’s a simple example, but so far I do like the minimal configuration and the ajax integration.

Update on Quant TestTester

Posted on | Comments

Here is a small update on my little fun project.

I released version 0.2 of quant. Now it will recognize all TestNG annotations which do not have a TestNG group (like @BeforeMethod, @BeforeClass etc.). The method ‘reportViolation’ on ClassTester will now report whats wrong with the examined class.

1
2
 assertFalse(classTester.isInvalidTestClass(),
    classTester.reportViolation());

Above code will now report:

java.lang.AssertionError: 
Report for Class com.maxheapsize.quant.testclasses.SetupMethodWithoutTestGroup
Ignore abstract classes: true
Specified TestGroups :  + testUnitTest
* Methods with wrong test group: 
  - testSetUp

In this way you know what went wrong and where to look for it.

Quant is now also available in my repository. To include it add the following code to your pom.xml.

[xml]

<groupId>com.maxheapsize</groupId>
<artifactId>quant</artifactId>
<version>0.2</version>

[/xml]

For now you need to add my mvn repository to your pom. I’m about to have it mirrored to the official servers.

Have fun.

Quant - Check Your Tests

Posted on | Comments

Did you ever wondered if all tests your team wrote are really running ? How many disabled tests does your code base have? How many public void methods do not have a @Test annotation (or at the class) ?

I saw all of that in the last years.

To overcome this situation I wrote a couple of java classes which will scan your java test sources and will examine the annotations (for TestNG, sorry JUnit). The code will detect if all public void methods do have a@Test annotation (either direct on the method or on the class) or if there are disabled tests. Both signals are most likely a sign of rotten code.

If you have different test groups defined like ‘unitTest’ or ‘integrationTest’ you want to make sure all tests are in at least one test group. This ensures if you run all test groups all tests are executed.

The usage is really simple. To make sure all your tests are ok is to write just another test which will check all tests.

1
2
3
4
5
6
7
ClassFinder classFinder =
   new ClassFinder.Builder(sourceDirectory).build();
for (Class klass : classFinder.getClassList()) {
  TestClassTester testClassTester =
    new TestNGTestClassTester.Builder(klass).build();
  Assert.assertTrue(testClassTester.allTestMethodsHaveValidTestGroup());
}

You need to specify the source files of your code so that all java files can be scanned. It is possible to exclude certain packages to be checked (via TestNGTestClassTester.Builder(klass).addExcludedPackage("testclasses").build()). After gathering all class names from the sources the code will check for @Test annotations in the test classes. You can also specify the test groups which all test should belong to (via addTestGroup("unitTest") in the builder).

Finding disabled test methods is very simple too (one of the unit tests)

1
2
3
4
5
6
@Test
public void testNoDisabledTest() {
  DisabledTestFinder unitUnderTest =
    new TestNGDisabledTestFinder.Builder(TwoTestGroups.class).build();
  assertFalse(unitUnderTest.hasDisabledTests());
}

This would test a single class (but you can of course feed it with the results of the ClassFinder).

If you combine the possibility to break your build because of defect test classes and a continuous integration system, you can make sure everybody will annotate their classes correctly or never disables tests (or even better use a CI with delayed check in and personal build like Jetbrains Teamcity). Of course you can define your own thresholds for e.g. 30 disabled tests are allowed (I know sometimes you just can’t avoid it).

The code depends on testng and commons-io and is released under the Apache v2 license.

I still need to figure out where to put the maven2 sources. Either Google code or GitHub ? Any ideas?

Update: Code is available at Google Code Hosting.

Things to implement:

  • A more flexible exclude patterm, like Apache ant maybe
  • Make sure to check @BeforeXXX and @AfterXXX methods as well
  • Implement JUnit (maybe ;-) )

Jetbrains Released a Google App Engine Plugin for IntelliJ

Posted on | Comments

Jetbrains released a Google App Engine plugin for their IntelliJ IDE. I did not see any announcement about this at all.

New project inside IntelliJ for Google App Engine

You will have a new option when creating a new project. After supplying the path to the local app engine SDK your almost good to go. I needed to add the app engine jars as libraries to my project. That being out of the way you can start coding, run you local server (a pre configured run configuration is supplied) and also deploy it to Google all from within IntelliJ. I tested it with a very basic example with local development and remote deployment and this works really nice.

Thanks Jetbrains.

From the release notes:

This plugin provides the following features:

  • option on “Technologies” page of the module wizard to quickly create:
    • appengine-web.xml descriptor
    • App Engine Facet
    • App Engine Dev server run configuration
  • inspection to report forbidden code in App Engine application
  • run configuration for Google App Engine Dev server
  • action for uploading an application to Google (Tools | Upload App Engine Application)

IntelliJ Idea Plugin Property Sorter Updated

Posted on | Comments

I’m pretty busy at work right now but I could find some time to add one more feature to my little property sorter plugin for IntelliJ Idea.

Comments above property entries will now be preserved when you sort the file.

The plugin is available from within Idea or at Google Code.

From unsorted ...

... to sorted with comments.