In this recipe, we will write a simple test using Spock that verifies the behavior of the system under test when an exception is thrown. Before going into details, it's worth mentioning that Spock is a Groovy-based (http://groovy.codehaus.org/) tool. Therefore, in order to use it, you need to know at least the basics of the Groovy language. Spock is based on JUnit and is much more than a mocking framework. It gives you a beautiful BDD (Behavior Driven Development) syntax that will convert your tests to Specifications (capital S since Specification
is a class that you need to extend to work with Spock).
If you want to try out Spock without installing it on your machine, check out the Spock Web Console at http://meetspock.appspot.com/, where you can write your tests online!
Spock is a perfect tool for you if you want to introduce Groovy into your project. You can start off with writing tests and then gradually progress towards production code (if that is what you want, of course). Spock's beautiful BDD approach, combined with the power of Groovy, makes it a perfect addition to your codebase.
To start working with Spock, you need to add it to your classpath. Remember that you also need to have Groovy attached. The following is the Groovy and Spock configuration for Gradle:
apply plugin: 'groovy' compile "org.codehaus.groovy:groovy-all:2.3.1" testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
The following is how you can add Groovy and Spock to your classpath using Maven (please see the Mockito Cookbook Github repo at https://github.com/marcingrzejszczak/mockito-cookbook for the exact setup for both Gradle and Maven):
<project> <build> <plugins> <plugin> <groupId>org.codehaus.gmaven</groupId> <artifactId>gmaven-plugin</artifactId> <version>1.5</version> <executions> <execution> <goals> <goal>testCompile</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.codehaus.gmaven.runtime</groupId> <artifactId>gmaven-runtime-api</artifactId> <version>1.5</version> <exclusions> <exclusion> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all-minimal</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>2.3.1</version> </dependency> </dependencies> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.spockframework</groupId> <artifactId>spock-core</artifactId> <version>0.7-groovy-2.0</version> <scope>test</scope> </dependency> </dependencies> </project>
As in previous recipes, the system under test will be the tax transferring system for a given person, as shown in the following code:
public class TaxTransferer { private final TaxService taxService; public TaxTransferer(TaxService taxService) { this.taxService = taxService; } public boolean transferTaxFor(Person person) { if (taxService.hasAlreadyTransferredTax(person)) { return false; } try { taxService.transferTaxFor(person); } catch (Exception exception) { System.out.printf("Exception [%s] caught while trying to transfer tax for [%s]%n", exception, person.getName()); return false; } return true; } }
To test the system using Spock, you need to perform the following steps:
Specification
class.Stub()
or Mock()
method. If you want to verify the mock's behavior, then use Mock()
. If you wish to only stub the execution, then use Stub()
.*
) and the count of wanted executions.The following snippet shows an example of a test with Spock (Spock as a dependency requires JUnit, and it's using a JUnit runner):
class TaxTransferrerSpec extends Specification { TaxService taxService = Mock() TaxTransferer systemUnderTest = new TaxTransferer(taxService); def 'should return false when tax was not transfered and connection to irs was refused'() { given: Person person = new Person() when: boolean transferSuccessful = systemUnderTest.transferTaxFor(person) then: !transferSuccessful 1 * taxService.hasAlreadyTransferredTax(person) >> false 1 * taxService.transferTaxFor(person) >> { throw new RuntimeException("Connection refused") } } @Unroll def "should return [#transferSuccessful] when tax wasn't already transferred and connection to irs was refused [#throwsException]"() { given: Person person = new Person() when: boolean transferSuccessfulResult = systemUnderTest.transferTaxFor(person) then: transferSuccessfulResult == transferSuccessful 1 * taxService.hasAlreadyTransferredTax(person) >> false 1 * taxService.transferTaxFor(person) >> { if(throwsException) { throw new RuntimeException("Connection refused") } } where: throwsException || transferSuccessful true || false false || true } }
We will not discuss how Spock works internally, but focus on what happens in the test itself, and what Spock's approach is.
Spock forces its users to follow the BDD approach (with the given
, when
, then
, expect
, and where
clauses), thus the tests become very clear and automatically separated into sections. As for the stubbing strategy, Spock has separate methods for stubbing and mocking. This is also to show explicitly the difference between the two. What is more, it's a Groovy framework (already a main testing framework for Grails), so you can profit from all of the Groovy magic, which should make your tests become really clean.
Let's take a look at the second Spock test that shows how easily you can define parameterized tests. The
@Unroll
annotation makes Spock insert values that are named according to the columns in the where
table, for each row of the where
part. In fact, throughout the test, you can refer to those values by the names of the columns. The >>
operator (right-shift operator) allows you to stub values with the result of the closure (the function defined within the curly braces). As you can see, we can even provide some more complex answers (like in the case of the transferTaxFor(…)
method stubbing). The 1 *
notation means that you want to verify that there was a single method execution.
Mockito's test code of the system would look like the following (example for JUnit):
@RunWith(MockitoJUnitRunner.class) public class TaxTransfererTest { @Mock TaxService taxService; @InjectMocks TaxTransferer systemUnderTest; @Test public void should_return_false_when_tax_was_not_transfered_and_connection_to_irs_was_refused() { // given Person person = new Person(); given(taxService.hasAlreadyTransferredTax(person)).willReturn(false); willThrow(new TaxServiceConnectionException("Connection refused")).given(taxService).transferTaxFor(person); // when boolean transferSuccessful = systemUnderTest.transferTaxFor(person); // then then(transferSuccessful).isFalse(); verify(taxService).hasAlreadyTransferredTax(person); verify(taxService).transferTaxFor(person); } }
The primary similarities between Mockito and Spock are as follows:
BDDMockito
)The primary differences between Mockito and Spock are as follows:
Specification
class to use Spock, whereas in Mockito you can use it straight away.then
or expect
block), which is really unintuitive.3.16.139.8