lunes, noviembre 19, 2012

Improving Readability of Your Conditional Expressions with Bool

Introduction

Bool is a project that uses Hamcrest matchers to provide a clean way to write conditionals and making them readable even for your clients.
Note that apart from cleaning your code, it forces you to maintain the level of abstraction of methods without having to populate your classes with a list of methods that are composed by one line returning a boolean value like next example:
...
if(areResultsAvailable(messages)) {
...
}

private boolean areResultsAvailable(List<String> messages) {
    return messages.size()>0;
}

From Hamcrest documentation we can read that:
Hamcrest it is not a testing library: it just happens that matchers are very useful for testing.
Hamcrest is used in many projects, JUnit for asserting tests (most of us have use it in this way), or Mockito. But as previous cite said,Hamcrest can be also used outside testing scope, Lambdaj is an example, and Bool is another one.

Installation

To use Bool you only have to add it to classpath.
<dependency>
    <groupId>com.lordofthejars</groupId>
    <artifactId>bool</artifactId>
    <version>0.9.0</version>
</dependency>

In Action

All required methods are provided as static methods in the class Bool:
import static com.lordofthejars.bool.Bool.*;
Let's explore a simple example to see the differences between using an if as usually and with Bool project:
String name = "Alex";

if(isAlex(name) {
...
}

private boolean isAlex(String name) {
    return "Alex".equals(name);
}
you can use Hamcrest directly in condition without using Bool, but see that readability is not improved:
import static org.hamcrest.CoreMatchers.equalTo;

if(equalTo("Alex").matches(name)) {
...
}
and finally using Bool:
if(the(name, is(equalTo("Alex")))) {
...
}
Note that the important word here is the, which receives the element to compare and a matcher (similar to assertThat method). See in previous example how condition has been improved so much.
Another place where Bool improves readability is with not operator (!).
If you want to return if name is not Alex, you should add a new method with ! operator which does not help us too much in improving the code legibility.
if(isNotAlex(name) {
...
}

private boolean isAlex(String name) {
    return "Alex".equals(name);
}

private boolean isNotAlex(String name) {
    return !isAlex(name)
}
But with Bool:
if(the(name, is(not(equalTo("Alex"))))) {
...
}
Bool is also useful when you are dealing with collections. Because you can use any matcher from Hamcrest project you can implement conditions that would require some work if you didn't use Bool, in a few seconds; see next example:
List ages = Arrays.asList(21, 25, 30);

if(areAllPersonsAdult(ages)) {
...
}

private boolean areAllPersonsAdult(List<Integer> ages) {

    for(int age:ages) {
        if(age < 18) {
            return false;
        }
    }

return true;

}
To something like:
List ages = Arrays.asList(21, 25, 30);

if(the(ages, is(everyItem(greaterThan(18))))) {
..
}
But also with arrays are improved so much, when you want to compare two arrays you cannot do by using equals method directly but using Arrays.equals method. But Hamcrest matchers deals with this problem, hiding this details from developers so we can compare arrays safely.
byte[] name = "alex".getBytes();

if(the(name, is(equalTo("Alex".getBytes())))) {
...
}
And of course you can use all matchers you can imagine like containsemptycontainsInAnyOrder, ...
Let's complicate things a bit more.
Normally, our conditions contain more than one clause. For example, the name should be "Alex" or "Ada"

For covering this case, be and is keyword is also available. is keyword provides a better readability to your conditions, but can conflict with Hamcrest "is" method. So be is also provided. You can use any of them.
if(the(name, be(equalTo("Alex")).or(be(equalTo("Ada"))))) {
...
}
and is also valid, but remember that the is method is from Bool class not the Hamcrest one.
if(the(name, is(equalTo("Alex")).or(is(equalTo("Ada"))))) {
...
}
be matcher also contains and operation too.
But sometimes rules are more complex and imply more than one variable. For solving this cases, we must use twice the the keyword. So for example if name should be "Alex" and surname "Soto":
String name = "Alex";String surname = "Soto";

if(the(name, be(equalTo("Alex")).and(the(surname, equalTo("Soto"))))) {...}
Note that be keyword is only mandatory when we want to concatenate conditions.
As final notes, you can even use matchers defined in Lambdaj project, and a pretty example could be to compare a property of a class:
private class Person {

        private String name;

        public Person(String name) {
            this.name = name;
        }

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

        public String getName() {
            return name;
        }
}

if(the(person, having(on(Person.class).getName(), is(equalTo("Alex"))))) {
...
}
And also you can use Bool in unit tests by using shouldBe method. For example a valid assertion could be:
assertThat(the(name, shouldBe(equalTo("Alex")).and(shouldBe(startsWith("A")))), is(true));

Performance

It seems reasonable that using Bool performance should be worst than using native conditions. But I have to say that I have been pleasantly surprised by the performance of Bool and Hamcrest matchers.
I have run 5000 times some examples provided in documentation. For example one single condition, one condition negated, two conditions over the same attribute with and keyword, two attributes being compared with one condition, and finally one collection comparison. And the results are the next ones:
Performance
Note that using simple conditions the time is the same, and is after we start creating more complex condition expressions that the performance is a bit reduced.
You can find the performance test in com.lordofthejars.bool.performance.PerformanceTests

Final Notes

Please keep in mind that the border between writing clean and readable code and something unintelligible is very thin. You can start creating a complex chaining of calls, train wreck, so no one can understand the real meaning of the condition. 

One good practice to avoid this case is splitting complex chaining calls into multiple variables and then the final calls are executed inside the if (or inside a method which extracts all this logic). But anyway if you found creating a really complex condition, think about the correctness of this logic before coding it, because maybe you are providing to a condition a heavy responsibility.
Also note that you can use Bool in all of your conditions, but where you really take the full power is in  conditions that represents important rules for your business.

Stay In Touch

And any suggestion, improve, or bug don't hesitate to open an issue.

E il Tacchino GluGluGlu, E il Gallo CoroCoco, E la Gallina Coo, E il Pulcino Pio (Il Pilcino Pio - I Blogger)
Music: http://www.youtube.com/watch?v=YNwjkEdBpOk

lunes, noviembre 05, 2012

Meet Me at Devoxx 2012



Next week starts the Devoxx. I will be there as speaker and as listener. You can meet me on two presentations (a Tools in Action and a BOF ones).




In Tools In Action I will introduce you NoSQLUnit, a JUnit extension for writing tests for NoSQL applications.

This will be on Monday 12th at 16:45.


Don't miss it if you are planning to use NoSQL systems as backend in your applications.




In the BOF, Bartosz Majsak, John Ferguson Smart, Paul Bakker, Dan Allen, Aslak Knutsen, Sarah White, Mircea Markus, Lukáš Fryč, David Blevins and Me, are going to talk about killing bugs.

This will be on Monday 12th at 21:00.


Don't miss it for anything else in the world.


Hope to see all of you there.
Alex.
And through it all she offers me protection, A lot of love and affection, Whether I'm right or wrong (Angel - Robbie Williams)



jueves, noviembre 01, 2012

NoSQLUnit 0.6.0 Released




NoSQLUnit is a JUnit extension to make writing unit and integration tests of systems that use NoSQL backend easier. Visit official page for more information.

In 0.6.0 release, one new NoSQL system is supported and is HBase.

Apache HBase is an open-source, distributed, versioned, column-oriented store.

As all databases supported by NoSQLUnit, two set of rules are provided to write HBase tests:

First set of JUnit Rules are those responsible of managing database lifecycle; basically starting and stopping HBase instance.
  • Embedded: com.lordofthejars.nosqlunit.hbase.EmbeddedHBase
  • Managed: com.lordofthejars.nosqlunit.hbase.ManagedHBase
Depending on the kind of tests you are implementing (unit test, integration test, deployment tests, …) you will require an embedded, managed or remote approach. Note that for now I haven't implemented an In-Memory approach because there is no in-memory HBase  instance, but embedded strategy for unit tests will be the better one. 

Second set of rules are those responsible of maintaining database into known state;
  • NoSQLUnit Management: com.lordofthejars.nosqlunit.hbase.HBaseRule
And finally default dataset file format in HBase is json. Dataset in HBase is the same used by Cassandra-Unit but not all fields are supported. Only fields available in TSV HBase application can be set into dataset.

We will use a very simple example used in HBase tutorial as an example of how to write unit tests for systems that uses HBase database as backend.

First of all, dataset used to maintain HBase into known state:


and finally the test case:


Stay in touch with the project and of course I am opened to any ideas that you think that could make NoSQLUnit better.
We de ze zu bu, We de sooo a ru, Un va-a pesh a lay, Un vi-I bee (Now We Are Free - Lisa Gerrard)
Music: http://www.youtube.com/watch?v=ObGYFInWrU0