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

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?

Advertisements