devNoo

musings of a Software Developer

A look at spock as junit replacement

| 0 comments

At work we are looking into frameworks from dynamic languages to help us test java code easier. I personally like groovy to use for java testing as it is so close to java, and so there is no big learning curve( something to consider when you work for bigger companies) . Also in our project (big legacy project) i had already introduced a dsl to setup test-data in groovy. I looked at the software kata from uncle bob in which he used rspec for testing, and was looking for something like this when I stumbled upon spock.

Spock is a testing framework written in groovy, that can easily test java and groovy code. It integrates fairly well with the maven 3 surefire plugin and can be used just by configuring the maven plugin and dependencies, and thus without having groovy installed on build machines.

In Intellij the integration is good, in eclipse it should also work after some struggles with plugins for groovy etc.

I will sum up some points i liked about spock.

Bdd like style of defining you test methods.

def "test for reverse string method"() {
   given: "an input with abc"
      def input = "abc"
   when: "it is reversed"
      def result = StringUtils.reverse(input)
   then: "it should be "cba"
      result == "cba"
}

You can also use and: ie

def  "test for concatenate strings" (){
   given:  "a string with text abc"
      def input1 = "abc"
   and: "a string with text def"
      def input2 = "def"
   when: "they are concatenated"
      def result = StringUtils.concatenate(input1, input2)
   then: " the result should be"
      result = "abcdef"
}

Parameterization

Especially when using baby steps to implement a method, test parameterization is a verry usefull feature. JUnit has a way to do this, by using the ParameterizedTestRunner. Apart from that the interface for doing this is kind of ugly, you can only use one type of parameterization per test class. So if you want to test different methods with parameterization you must create multiple test-classes.

In Spock it is much easier:

def "tests for reversing a string"()

		when:
			def result = StringUtils.reverse(input) < /code>
   		then:
	   		result == expected
	   	where:
			input		| expected
			""			| ""
      		"abc"		| "cba"
      		null		| null
      		"reverse" 	| "esrever"
      }


this table form of defining the parameters is quite readable, if for some reason it is not convenient, you can define them by the following:

where:
	input << ["", "abc", null]
	expected << ["", "cba" , null]

Keep in mind that the where block can’t access your instance variables, and you can’t create mocks(using spock that is, Mockito mocks can offcourse be used).

Exception:

Like in junit there also is a way to tell spock an exception is expected:

when: "concatenate is called with a null argument"
	StringUtils.concatenate("abc", null)
then: "an illegal argument exception should be thrown"
	thrown(IllegalArgumenException)

It is also easy to inspect the exception:

when: "concatenate is called with a null argument"
	StringUtils.concatenate("abc", null)
then: "an illegal argument exception should be thrown"
	def e = thrown(IllegalArgumenException)
	e.message == " null argument not allowed"

Mocking

Spock includes its own mocking framework, which is quite a simple and eassy to use mocking solution. Next to its own framework you are also able to use Mockito and other mocking frameworks.

Spocks way of defining a mock:

def mockedPersonDao = Mock(PersonDao)

or

PersonDao mockedPersonDao = Mock()

the behavior of a mock can be defined in the setup block(or the given and “and” alias) like this:

def "test for FindPerson"(){
	setup:
		def mockedPersonDao = Mock(PersonDao)
		PersonService personService = new PersonService()
		personService.setPersonDao(mockedPersonDao)
		personDao.findPerson("lucas") &gt;&gt; new Person("lucas")
	when:
		def result = personService.findPerson("lucas")
	then:
		result.name == "lucas"
		result.age == 0
}

it is also possible to define the behavior and verify that the methods are called at the same time

def "test for FindPerson"(){
	setup:
		def mockedPersonDao = Mock(PersonDao)
		PersonService personService = new PersonService()
		personService.setPersonDao(mockedPersonDao)
	when:
		def result = personService.findPerson("lucas")
	then:
		1 * personDao.findPerson("lucas") &gt;&gt; new Person("lucas")
		result.name == "lucas"
		result.age == 0
}

Now exactly one call is expected and the person is returned when this happens.

If you want to return a different result each time the method is called and you don’t care about the argument, you can use this:

3 * personDao.findPerson(_) &gt;&gt;&gt; [new Person("lucas"), new Person("julie"), new Person("binck")]

The mocking framework is quite evolved, but sometimes i still miss some features of Mockito, like argument captors etc.

conclusion
All things regarded i would really recommend java developers to give spock and with it groovy a try to test your application. Spock delivers a nice bdd style for creating tests, has excellent parameterization options and decent built in mocking support. Groovy will also provide you with a lot of time savers in comparison to java, while it still is a verry simple language to learn for a java developer.

I didn’t try to create integration tests with it, so i don’t know if it is integrates with spring etc, but for unit tests it is excellent.

´╗┐

Leave a Reply

Required fields are marked *.