Archive

Archive for the ‘Scala’ Category

Writing a BDD DSL in Scala

November 17, 2009 1 comment

One of my favourite BDD frameworks is easyb. I really like the dsl for user stories. In a previous post called Easy Requirements by Example I discussed how easy and clear this dsl can be.

For Scala I would also like to have a dsl just like the one of easyb. Writing a dsl in Scala is real easy, not as easy as in Groovy, but easy enough. So I decided to see how fast I could write a story dsl in Scala.

So, I need to be able to write things like the following

   given ("a blogger with a blog") {
    	val blogger = new Blogger("Mr Blog", "mrblog@blogger.com", "passwd")
    	val blog = new Blog("blogTitle", blogger)

		when("mr blog writes a new post") {
			blog addPost ("post Title", "post Text");
		}
		and
		when("mr blog publishes its post") {
		    blog publishPost
		}
		then("the post should be publised for reading") {
			blog.published shouldHave "post Title"
		}
	  }

The words given, when, then, and, shouldHave are part of the dsl.
The story dsl words are defined by a curried function. They have a explaining piece of text as the first argument and a code block as the second argument.

See below the definition of given

  def given(text: String)( codeBlock: => Unit ) = {
	  codeBlock;
  }

Because given is a curried function you can pass in the arguments one at a time. And when you pass in one argument you can choose to use curly braces instead of parentheses. As you can see in the above example, the given function has the rest of the story as the second argument within curly braces. So, the when, then, and form the codeBlock argument.

Here is the definition of the other methods

  def then(text: String)( codeBlock: => Unit ) = {
    codeBlock;
  }
    
  def when(text: String)( codeBlock: => Unit ) = {
    codeBlock;
  }
    
  def and(text: String)() = {
  } 

… Yep this is really simple!

Because the codeBlock parameters are so-called “By-name-parameters” the codeBlocks of the functions are only evaluated when the given method evaluates it’s codeBlock! Also because closures in Scala see changes made to the free variables outside of the closure itself, you get the sequential execution ordering of the story.

The only thing that is left is the shouldHave word. As you might have guessed this is just a simple implicit conversion. To have free integration with IDE’s and other tools I choose to use JUnit as the underlying framework. So all the dsl vertification words are mapped to JUnit asserts as you can see below.


  implicit def shouldHave(l: List[Any]) = {
	  ShouldHave(l)
  }

case class ShouldHave(i: List[Any]) {
    def shouldHave(j: Any) = {    	
    	assertEquals(j, i.find( _ == j))
    }    
}

You can use the dsl in your JUnit test like this

@Test
def bloggerSpecification() = {

given ("a blogger with a blog") {
…..
}
}

You can imagine that you would like to execute a story multiple times with different argument values. Lets say that you would like to execute the story with multiple post titles. A little extension of the dsl made that possible. I introduced the words testcases, addCase and execute_testcases_with_specification.
See below an example

    testcases {
    	var postTitle: String = null;
    	
    	addCase { postTitle = "title 1"	}
    	addCase { postTitle = "title 2"	}
    	
    	execute_testcases_with_specification {
    		given ("a blogger with a blog") { 	
    			val blogger = new Blogger("Mr Blog", "mrblog@blogger.com", "passwd")
    			val blog = new Blog("blogTitle", blogger)		   
		        
    			when ("mr blog writes a new post") { 
    				blog addPost (postTitle, "post Text");			   
    			}
    			and
    			when ("mr blog publishes its post") {
    				blog publishPost
    			}
    			then ("the post should be publised fo reading") {
    				blog.published exists(_.title == postTitle) shouldBe true			
    			}		   
    		} 	
    	}
 

What you see is that there are two test cases, each specifying a different value for the postTitle. The story is executed for each test case where the free variables are changed according to the test case at hand.

In order to make this work I stored all the test cases in a list. Then for each test case I first evaluate the test case. This sets the free variables of the story closure. Next I evaluate the story itself.

Again the code is really simple as you can see below.

object TestCase {

  var codeBlocks = List[() => Unit]()
  var scenario: Any = 0;
  
  def addCase( codeBlock: => Unit) = {
	  codeBlocks = codeBlock _ :: codeBlocks
  }
     
  def execute_testcases_with_specification( scenario: => Unit) = {
    codeBlocks.foreach( codeBlock => {
      codeBlock();
      scenario;
    })
  }
  
  def testcases( codeBlock: => Unit) = {
	  codeBlock
  }
}

If you made it this far in the post… what do you think of this approach?

Advertisements
Categories: BDD, Scala Tags: ,