Hot shot 005 – Quick introduction Spock Testing framework
24 May 2018 Comments off
Reading time:
7 minutes
Word count:
1459
This are my verbatim notes to the PEAT UK podcast:
Hot shot 005 – Quick introduction Spock Testing framework
Thursday, 17th May 2018
Hello there once again to another hot shot. My name is Peter Pilgrim, Platform engineer and DevOps specialist, and Java Champion.
Spock is a behavourial-driven design (BDD) testing framework written in the Groovy programming language. Obviously, it works with Groovy, but Java developers can use to test their applications.
Spock is based on the idea of Specifications. A specification is a written definition of Feature that a system on the under test should implement. Features are properties, functions and other aspects of behaviour that a system exhibits.
Let’s take an example, if you are writing a e-commerce system with a shopping cart. If you start with an empty shopping cart, then the total price of the cart is £0. This might be feature number 1. If you add a basic product item such John Smith iron widgets at a bag of 100 for £9.99, then the total of the cart is £9.99. If you add another item of the same then the total would be £19.98. We now three features under test. Now let’s take things further, suppose the store wants to fulfill a BOGOF promotion for all John Smith ironmongery for this month, then you can add a third bag of iron widgets and the total cart should still be £19.98.
As you can see BDD is a product owner and as business analyst wet dream. The specification represents the business rules for the working application and this is why BDD is really loved by Agile practitioners.
So Spock reminded me of Cucumber in Ruby and also the JVM version of this framework, which is called Cucumber JVM. I first came across Cucumber as a separate UI/UX testing framework for a GOV.UK project that help bring into this reality, but I digress…
Spock specifications are written in the Groovy language. In order to use it in a Java 8 project, you simply need to add Spock dependencies to your Gradle or Maven project files.
So if you using Gradle as your build system, then you need to add the Groovy plugin to your build by applying it.
apply plugin: 'java' apply plugin: 'groovy'
Afterwards, you add Groovy Language ALL dependencies and also then the Spock Core dependencies. You might find you need to add other dependencies such as Hamcrest, JUnit and Spock Reports
testCompile 'commons-io:commons-io:2.6' testCompile "org.codehaus.groovy:groovy-all:2.4.11" testCompile "org.spockframework:spock-core:1.1-groovy-2.4" testCompile "org.spockframework:spock-spring:1.1-groovy-2.4" testCompile "cglib:cglib-nodep:2.2" testCompile "org.hamcrest:hamcrest-junit:2.0.0.0" testCompile "junit:junit:4.12" testRuntime "com.athaydes:spock-reports:1.2.7" testRuntime "com.h2database:h2"
Under Gradle, the standard location for Groovy code is src/main/groovy
. You write your first Groovy Spock specification in this project tree. The first line of your file will be
import spock.lang.*
You then write a class called MyShoppingCartSpecification
that extends Spock’s Specification
class.
Inside this class, you can add fields that instantiate the class under test and collaborators.
If you happen to use a dependency injection framework like Spring, then you auto-wire in dependencies. Spock works very well with the Spring Framework including the MVC framework.
You can start writing feature tests with defining custom groovy methods. You can write readable feature names with the double quoted strings.
def "creating an empty shopping cart must be empty"() { }
Feature methods are the heart of the specification. Each feature is broken down into four steps
Set up the feature’s fixtures, if any (acquire the resources)
Configure the messages that will be injected into the system under test (this is the object that we are testing i.e ShoppingCart )
Describe the response that we expect from the system under test
Clean up the residue of the fixture, if any (release the resources)
Specification under Spock has this concept of Blocks. These are syntactic areas of code where to stuff configuration, setup resources and define feature behaviour. These Block are deliberately designed to be reasonable, concise and readable to a non-technical reader. (The secret sauce of this is Domain Specification Language, DSL programming that those clever Spock developers wrote with the flexible Groovy. It is the reason why Groovy has been loved Beyond Java engineers since the year 2005. Again, I digress … this is your homework)
You can write a sample feature like this on the radio
def "add a single item to the shopping cart"() { setup: def shoppingCart = new EcommerceShoppingCart() def productOne = new Product(items = 1, "John Smith", "Iron Widgets 20 per bag”, “5.99” ) when: shoppingCart.add(productOne) then: !shoppingCart.empty shoppingCart.size() == 1 shoppingCart.totalPrice == 5.99G // 5.99G is Groovy handy syntactic sugar for java.lang.BigDecimal notation! \o/ } [/code/ There are other blocks like clean up cleanUp: shoppingCart.releaseResources()
There is also a With method that accepts a target object and a Groovy closure method. You can think of the second parameter as a Lambda method that returns a void. You can write a simpler THEN blocks with the following code:
then: with (shoppingCart) { empty == false size == 1 shoppingCart.totalPrice == 5.99G }
And finally, I will describe the killer feature for Spock is data-driven testing. This concept allows writing tabular input test data for our system under the test and then verify the system behave correctly. We can write a data-driven with two additional BLOCKS called EXPECT and WHERE.
def "Algorithm for the Fibonnacci series"(int a, int b) { expect: calculateFibonnaci(a) == b where: a | b 1 | 1 2 | 1 3 | 2 4 | 3 5 | 5 6 | 8 7 | 13 }
Now you understand Spock’s goodness. I would recommend that you read up and learn about Groovy mocking capabilities, meta object programming and then read Spock’s own mock object framework.
In most cases, a method that returns a result in a Spock Mock object will return a default answer. Rather than throw a runtime exception, it will return false, 0 or an empty string. Spock’s philosophy is called lenient mocking. However, engineers can customise the mock object method call by stubbing out with a value. Spock makes use of the operator overloading to be readable and literate.
Let suppose, we have a micro service that implements a payments engine provider. It provides an API captured in a Java interface. We assume there a concrete client implementation of this interface. Thes DDD types that reflect the business requirements:
interface PaymentsProvider class CreditCardPaymentsProvider implements PaymentsProvider
We can write Spock specification that creates a Mock object of this contractual interface. Spock can Mock concrete classes just like Mockito can.
setup: def paymentsProvider = Mock(CreditCardPaymentsProvider)
We can then make use of the GIVEN block to sub out the method, which the shopping cart makes to the payments provider.
given: paymentsProvider.requestPayment( _ ) >> true
We can write a WHEN and THEN block clauses.
when: shoppingCart.addPricingEngine(promotionTask) shoppingCart.addItems( new Product( items=1, "John Smith", "Iron Widgets 20 per bag", 5.99G ) def result = shoppingCart.processCart() then: result == true
Ok this is slightly contrived for the purpose of illustration. A proper business application would be making RESTful calls and using a dependency injection framework. Trust me with enough, tweaking you can write some stunning refreshing and comprehensive tests with Spock.
Spock has very powerful mocking abilities that make it useful for hybrid unit test and integration testing. You can then on the way to writing precise, contract aligned and comprehensive suite of tests for your microservice application architecture.
@SpringBootTest class ACMEPaymentSpecification extends Specification { @Autowired private AcmePaymentsSink acmePaymentsSink @Autowired private PersistentStorageStatusUpdateSink persistentStorageStatusUpdateSink @Unroll def 'Must persist the payment status to storage and notify GPDR service'() { when: notifier = Mock(GPDRNotificationService) expect: acmePaymentsSink.input().send(buildDefault(paymentId)) persistentStorageStatusUpdateSink.input().send(buildStorageMessage(processId, responseUpdate)) def invalid = messageCollector.forChannel(invalidPaymentsFlowSource.output()).poll() def valid = messageCollector.forChannel(acceptedPaymentsFlowSource.output()).poll() hasInvalidMessageInQueue == (invalid != null) hasSuccessMessageInQueue == (valid != null) persistentStorageRepository.findOne(paymentId) == null then: 3 * notifier.notifier("GPDR Notice") where: paymentId | hasInvalidMessageInQueue | hasValidMessageInQueue | responseUpdate "EU1001" | false | true | PaymentStatus.ACCEPTED "EU1002" | true | false | PaymentStatus.INVALID "EU1003" | true | false | PaymentStatus.REJECTED } }
Here is very hot tip: You probably want to make use of Spock’s @Unroll annotation in data-driven feature test.
That’s all from me for now. Enjoy your platform engineering and DevOps working day wherever you are in the world.
Bye.
+PP+
May 2018
By the way, all your Shares, Likes, Comments are always more than welcomed!