Skip to Content

Introduction

Back in last October, I hosted a webinar session as part of the 24-Hour Mentor Magic Marathon. The recording for the session was supposed to be made publicly available, but unfortunately it is not meant to be due to technical issues. Therefore, this blog aims to reproduce the core content that was delivered during the webinar session.

 

The session was a follow-up from my previous blog, (How) Do You Test Your Groovy Scripts?, where the premise is that currently it is “groovier” and more efficient to test your Groovy scripts offline in Eclipse.

 

Groovy meets Spock

Now that we have the capability to test Groovy scripts locally in Eclipse, that should not be the end of it. The next goal should be finding an efficient way to test the scripts. Everyone wants to run Agile these days, and without an efficient and automated approach, running Agile is simply not possible.

 

This is where Spock comes in.

 

No, not that Spock! This Spock!

  • A testing framework ideally suited for Groovy developments
  • Enables Test Driven Development (TDD) approach
  • Easy to incorporate into Eclipse
    • Include single Spock library file into build path
    • Executable as JUnit Test
  • Provides “groovy” results

 

To boldly test where none has tested before

Following is a reproduction of the demo during the session. In this demo, I use Spock to implement TDD-based approach to develop and test a Groovy script.

The overall requirement (or specification in Spock-speak) for the Groovy script is to extract the key-value pairs from an HTTP query string. These parameter values should then be stored as message properties. Following are the concrete requirements for the script:-

  • message property is populated with single parameter from query string
  • message properties are populated with multiple parameters from query string
  • message property is populated when special characters are included in query string
  • message property is populated when query string contains OData query string options

 

First of all, download and include the spock-core library in the build path.

Next, begin by creating a new Groovy class, ExtractQuerySpockTester.groovy, and as a start, I will just implement the first test case (called feature method in Spock).

To simulate the test condition, a parameter key-value pair (mode=test) is passed in the CamelHttpQuery header that normally stores the query string from a sender HTTP adapter. After the script is executed, the message property is inspected to verify that it contains the key-value pair.

 

The script under test remains the same Groovy script from the previous blog (default Groovy script created in CPI).

When I run the Spock test as a JUnit test (select the Spock test class, Right Click > Run As > JUnit Test), the following error will be displayed – expected as no relevant logic has been implemented yet.

 

 

In TDD fashion, I implement the simplest logic to achieve a successful test case. The following logic retrieves the content of the CamelHttpQuery header, splits the key-value pair by the = delimiter and stores it as a message property.

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;

def Message processData(Message message) {
	
	String httpQuery = message.getHeaders()["CamelHttpQuery"]
	int index = httpQuery.indexOf("=")
	message.setProperty(httpQuery.substring(0, index), httpQuery.substring(index + 1))
	
	return message
}

 

When I rerun the Spock test, the results are successful in the JUnit console.

 

Results are Groovier with Spock

Now, I include the full blown Spock test which includes the test cases for all the requirements – the complete source code is available in the Appendix section below.

Once I rerun the Spock test, the JUnit console displays the results for all four test runs. The first test remains successful, but the remaining have errors (again as expected). Another nice feature of Spock is the way the results are displayed. As shown in the screenshots below, Spock displays the value of each object of the expected condition, as well as how it differs from the expected results. The results are shown in a simple and easy-to-view manner.

 

To complete the demo, I proceed to enhance the logic to fulfill each new test case (while making sure previous ones are still successful) in an iterative manner.

The logic is modified to split the query string by the & delimiter to separate out the key-value pairs, and then loop through each of them to store them as message properties.

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;

def Message processData(Message message) {
	
	String httpQuery = message.getHeaders()["CamelHttpQuery"]
	// Split parameters by &
	String[] parameters = httpQuery.split("&")
	parameters.each {
		int index = it.indexOf("=")
		message.setProperty(it.substring(0, index), it.substring(index + 1))
	}

	return message
}

With this, the first and second test cases are successful.

 

Lastly, to handle the special characters, I defined and used a closure, decodeQuery, to decode any special characters in the query string.

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;

def Message processData(Message message) {
	
	String httpQuery = message.getHeaders()["CamelHttpQuery"]
	// Split parameters by &
	String[] parameters = httpQuery.split("&")
	parameters.each {
		int index = it.indexOf("=")
		def decodeQuery = { input ->
			URI uri = new URI("http://localhost?" + input)
			return uri.getQuery()
		}
		message.setProperty(it.substring(0, index), decodeQuery(it.substring(index + 1)))
	}

	return message
}

 

Finally, all the test cases are successful now.

 

Conclusion

With Groovy being a mainstay in CPI, ensuring that Groovy scripts are developed and tested in an effective and efficient manner becomes crucial. Spock provides an easy-to-use, elegant and powerful framework to enable this. As shown in the example above, Groovy scripts can be developed in a TDD-based iterative manner, and new requirements can be easily incorporated while ensuring it does not break any existing functionality.

 

Appendix

Source code for Spock specification:-

package com.equalize.groovy.testing

import com.sap.gateway.ip.core.customdev.processor.MessageImpl
import com.sap.gateway.ip.core.customdev.util.Message
import spock.lang.Shared

class ExtractQueryParametersSpockTester extends spock.lang.Specification {

	@Shared GroovyShell shell = new GroovyShell()
	@Shared Script script
	private Message msg
	
	def setupSpec() {
		// Load Groovy Script		
		script = shell.parse(new File("src/com/equalize/groovy/testing/script1.groovy"))
	}
	
	def setup() {
		this.msg = new MessageImpl()
	}
	
	def "message property is populated with single parameter from query string"() {
		
		given: "a single parameter is in the query string"		
		this.msg.setHeader("CamelHttpQuery", "mode=test")
			
		when: "we execute the Groovy script"
		script.processData(this.msg)
		
		then: "we get the message property populated with the parameter value"
		this.msg.getProperties()["mode"] == "test"
	}
	def "message properties are populated with multiple parameters from query string"() {
		
		given: "multiple parameters are in the query string"	
		this.msg.setHeader("CamelHttpQuery", "firstname=engswee&surname=yeoh&salutation=mr")
		
		when: "we execute the Groovy script"
		script.processData(this.msg)
		
		then: "we get the message properties populated with the parameters' value"
		this.msg.getProperties()["firstname"] == "engswee"
		this.msg.getProperties()["surname"] == "yeoh"
		this.msg.getProperties()["salutation"] == "mr"
	}
	def "message property is populated when special characters are included in query string"() {
		
		given: "special characters are included in query string"
		this.msg.setHeader("CamelHttpQuery", "input=this%26that")
		
		when: "we execute the Groovy script"
		script.processData(this.msg)
		
		then: "we get the message properties populated with the decoded special characters"
		this.msg.getProperties()["input"] == "this&that"
	}
	def "message property is populated when query string contains OData query string options"() {
		
		given: "OData query string options are included in query string"
		this.msg.setHeader("CamelHttpQuery", '$filter=firstname%20eq%20engswee')
		
		when: "we execute the Groovy script"
		script.processData(this.msg)
		
		then: "we get the message properties populated with the decoded OData query string content"
		this.msg.getProperties()['$filter'] == "firstname eq engswee"
	}
}

 

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply