bits and pieces

October 27, 2010

easyb BDD framework – isA(“simple Groovy DSL”)

Filed under: Uncategorized — Tags: , — roshandawrani @ 9:10 am

Came to know y’day that easyb – a BDD (Behaviour Driven Development) framework – is groovy based and got eager to see how it is implemented, what groovy techniques is its DSL based on (does it also use AST transformation like Spock?), how does it make the groovy compiler support its *.story scripts, how does it support the following structure in its tests, etc?

scenario "any kind of scenario description", {
    given "describe the initial test state", { ... }
    when "actions that change things", { ... }
    then "post action verification", { ... }
}

The actual implementation, in terms of groovy features it uses, turned out to be simple-ish – no fancy AST transforms used, no tricky configuration of groovy compiler to make it understand that a *.story file contains a groovy script.

Easyb supports *Story.groovy and *.story files as the BDD scripts. When you run the easyb tests as below, it picks up all files ending with *Story.groovy and *.story from the list of scipts it is fed, reads their text and feeds that text to the groovy compiler as plain String. So as far as groovy compiler is concerned, it sees no stories.

java -cp classes;easyb-0.9.8.jar;lib\commons-cli-1.2.jar;lib\groovy-all-1.7.5.jar org.easyb.BehaviorRunner <somepath>\AccountsTest.story <somepath>\TransactionStory.groovy

Then comes the DSL part of it.

Nothing fancy here as well. Every story file that easyb processes gets a new Binding with a number of closures in the binding, for ex, scenario, given, when, then, and, but, before_each, after_each, etc. So, when you execute the following story:

scenario "money is withdrawn from an account 1 containing sufficient funds", {
    given "an account with 100 dollars", {
        account1 = new Account(100);
    }
    and "another account with 200 dollars", {
        account2 = new Account(200);
    }
    when "account holder withdrawls 20 dollars from account 1 and 40 dollars from account 2", {
        account1.withdrawl 20
        account2.withdrawl 40
    }
    then "account 1 should have 80 dollars left", {
        account1.balance.shouldBe 80
    }
    and "account 2 should have 160 dollars left", {
        account2.balance.shouldBe 160
    }
}
scenario "money is withdrawn from an account with insufficient funds", {
    given "an account with 10 dollars", {
        account = new Account(10);
    }
    when "withdraw more than the account has", {
        withdrawl = {account.withdrawl 20}
    }
    then "account holder should be notified about the insufficient funds", {
        ensureThrows(RuntimeException){
            withdrawl()
        }
    }
    and "then account should still have 10 dollars", {
        account.balance.shouldBe 10
        account.balance.shouldBeA(Integer)
    }
}

then easyb records the above test as 2 main test steps –

  • Scenario 1 closure – having 5 child steps [2-given, 1-when, 2-then closures], and
  • Scenario 2 closure – having 4 child steps [1-given, 1-when, 2-then closures]

Execution of the above story script results in all the closures getting stored in the script binding and then the same are played back (scenario closures are executed in sequence with their child closures executed in sequence). Data defined in “given” steps gets stored as script binding variables and is accessible to further steps like “when”, “then”, and “and”.

Last bit that remains is the following

account.balance.shouldBe 10
account.balance.shouldBeAn Integer

This is simply achieved through the use of groovy categories feature. All the story steps are executed in the scope of BehaviorCategory, which enhances java.lang.Object with the operations like following: shouldBe, shouldBeGreaterThan, shouldBeLessThan, shouldBeEqual, shouldntBe, shouldBeA, shouldNotBeA, shouldNotHave, etc, which means that in easyb stories, these operations can be called on any type of object whatsoever.

Just some regular groovy features seem to be enough for this BDD framework – script bindings, closures, categories, regular scripts compilation.

easy(b), isn’t it?

October 18, 2010

Groovy (New feature): Closures can now memo(r)ize their results

Filed under: Uncategorized — Tags: — roshandawrani @ 12:38 pm

Groovy recently added a new feature to its closures – memoization – a simple yet very useful feature that can drastically improve the performance when applicable.

It was a shame that I didn’t know the meaning of this term that was coined way back in 1968 by Donald Mitchie (ref: http://en.wikipedia.org/wiki/Memoization) [But now that I have memoized it, next time I am asked what it is, I will reply faster! :-)]

So, all this new feature means is that a groovy closure can now remember the result of its invocation for a specific set of inputs – by internally caching the result. So, the first invocation for a given set of inputs does the actual work, then the result gets cached behind the scenes and any further invocations with the same inputs simply return the cached result, making the invocation as fast as it can be. Of-course, this feature should be used only when closure processing is such that a set of inputs will ALWAYS result in the same output – it MUST be side-effect-free.

Here is an example to illustrate the use. Let’s first see it without memoization:

cl = {a, b ->
    sleep(3000) // simulate some time consuming processing
    a + b
}

def callClosure(a, b) {
    def start = System.currentTimeMillis()

    cl(a, b)

    println "Inputs(a = $a, b = $b) - took ${System.currentTimeMillis() - start} msecs."
}

callClosure(1, 2)
callClosure(1, 2)
callClosure(2, 3)
callClosure(2, 3)
callClosure(3, 4)
callClosure(3, 4)

Here is the output showing that everytime the time cost of closure invocation in incurred:

Inputs(a = 1, b = 2) – took 3041 msecs.
Inputs(a = 1, b = 2) – took 3000 msecs.
Inputs(a = 2, b = 3) – took 3000 msecs.
Inputs(a = 2, b = 3) – took 3000 msecs.
Inputs(a = 3, b = 4) – took 3000 msecs.
Inputs(a = 3, b = 4) – took 3000 msecs.

Here is the memoized version:

cl = {a, b ->
    sleep(3000) // simulate some time consuming processing
    a + b
}
mem = cl.memoize()

def callClosure(a, b) {
    def start = System.currentTimeMillis()

    mem(a, b)

    println "Inputs(a = $a, b = $b) - took ${System.currentTimeMillis() - start} msecs."
}

callClosure(1, 2)
callClosure(1, 2)
callClosure(2, 3)
callClosure(2, 3)
callClosure(3, 4)
callClosure(3, 4)

callClosure(1, 2)
callClosure(2, 3)
callClosure(3, 4)

Here is the output showing how only the first invocation incurs the processing cost and after that the calls with the same inputs are almost free:

Inputs(a = 1, b = 2) – took 3054 msecs.
Inputs(a = 1, b = 2) – took 0 msecs.
Inputs(a = 2, b = 3) – took 3001 msecs.
Inputs(a = 2, b = 3) – took 0 msecs.
Inputs(a = 3, b = 4) – took 3000 msecs.
Inputs(a = 3, b = 4) – took 0 msecs.
Inputs(a = 1, b = 2) – took 0 msecs.
Inputs(a = 2, b = 3) – took 0 msecs.
Inputs(a = 3, b = 4) – took 0 msecs.

The trade-off involved is that you pay for time saving in terms of extra memory consumed by the internally cached inputs/outputs mappings. If you want to have more control over how many results you want to cache, other variants to memoize() are – memoizeAtMost(n), memoizeAtLeast(n), memoizeBetween(m, n)

To quickly see how memoizeAtMost(n) works, in the previous example, let’s replace mem = cl.memoize() by mem = cl.memoizeAtMost(2) – and the output changes as below (now only 2 results are cached, with the LRU ones evicted):

Inputs(a = 1, b = 2) – took 3051 msecs.
Inputs(a = 1, b = 2) – took 0 msecs. /* 1st result – cached! */
Inputs(a = 2, b = 3) – took 3000 msecs.
Inputs(a = 2, b = 3) – took 0 msecs. /* 2nd result – cached !! */
Inputs(a = 3, b = 4) – took 3001 msecs.
/* 3rd result – cached – but now (a = 1, b = 2) result evicted – on LRU basis */
Inputs(a = 3, b = 4) – took 0 msecs.
/* (a=1, b=2) got evicted, processing happens again and result cached, now evicting (a=2, b=3) result */
Inputs(a = 1, b = 2) – took 3000 msecs.
/* (a=2, b=3) got evicted, processing happens again and result cached, now evicting (a=3, b=4) result */
Inputs(a = 2, b = 3) – took 3000 msecs.
/* (a=3, b=4) got evicted, processing happens again */
Inputs(a = 3, b = 4) – took 3000 msecs.

Good feature, isn’t it?

Note: The new feature is only available from groovy version 1.8-beta-3 onwards.

October 16, 2010

Groovy++ Goodies (1) – Closure Level Fields

Filed under: Uncategorized — Tags: , — roshandawrani @ 9:58 am

Groovy++ allows you to declare closure level fields using its new @Field annotation.

In core groovy, if you need to remember some state across multiple calls to the same closure instance, you need to do it using variable declared outside the closure scope. So, I see this Groovy++ feature as a useful goodie.

Below is some sample code to demonstrate the use of closure level fields:

@Typed void squares() {
    (1..20).each {
        @Field boolean lastOneSqaure = false
        @Field int which = 0
        
        if(lastOneSqaure) println "$it) Last number($which) was a square"
        
        double d = Math.sqrt(it)
        if((d - ((int) d) == 0)) {
            lastOneSqaure = true
            which = it
        } else {
            lastOneSqaure = false
        }
    }
}

squares()

The above code correctly outputs

2) Last number(1) was a square
5) Last number(4) was a square
10) Last number(9) was a square
17) Last number(16) was a square

The groovy++ compiler makes sure that the variables marked with @Field are declared as fields in the closure class and not as local variables – allowing you to preserve state across multiple invocations to a particular closure instance without needing any out-of-closure-scope assistance.

The Silver is the New Black Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: