Spock/Groovy Integration Tests
With the 4.1 release of Spring, we now have the ability to test RESTful services and other integration test abilities in a much simpler format. With a few annotations, we can quickly write integration tests that utilize a Spring generated application context.
Pom Setup
To use this new method of integration test creation, you will need to modify your pom with the following dependencies and plugins.
Dependencies
Before we move into the test class's configuration, we need to include some dependencies in the project's pom.xml. Please make sure you have the following:
...
<repositories>
<repository>
<id>maven central snapshots</id>
<name>maven central snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
...
<dependency>
<groupId>org.broadleafcommerce</groupId>
<artifactId>integration</artifactId>
<version>3.2.0-SNAPSHOT</version>
<type>jar</type>
<classifier>tests</classifier>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.broadleafcommerce</groupId>
<artifactId>integration</artifactId>
<version>3.2.0-SNAPSHOT</version>
<type>jar</type>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.1.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.0-groovy-2.0-SNAPSHOT</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>test</scope>
</dependency>
Plugins
With the dependencies, we want to use some new plugins to help with testing in general. These are the Surefire and Failsafe plugins. They have the following pom entries:
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<configuration>
<includes>
<include>**/*Spec*</include>
</includes>
<excludes>
<exclude>**/browsertests/**</exclude>
<exclude>**/*IT*</exclude>
</excludes>
<properties>
...
</properties>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.17</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
<include>**/*IT*</include>
</includes>
</configuration>
</plugin>
...
</plugins>
...
</build>
Make sure you have the GMaven plugin set up or else it won't recognize the groovy files.
With these plugins, we can now do the following:
- When we run
mvn clean install
it will run all both the unit tests specified by the Surefire plugin, as well as the the integration tests (which must include IT in their name) specified by the Failsafe plugin. - If you do not want to include running the integration tests, use
mvn clean install -DskipITs
to have it just run the unit tests and ignore the integration tests. You may want to use this when you are just testing your unit tests during their creation and don't want to run the longer integration tests.
Test Spec Setup
We are now using a new format for the Spock/Groovy tests. We now have an idea of an IntegrationSetup
file and an ITSpec
file.
IntegrationSetup
IntegrationSetup
files extend from base setup files on a per project basis and add all of the application Contexts that are required by the project. Every Project's IntegrationSetup
file will extend from either AdminIntegrationSetup
or SiteIntegrationSetup
depending upon which kind of integration test you are attempting. For example, in the CustomField module, we created an integration test that interacts on the admin side, so we create a CustomFieldAdminIntegrationSetup
file as follows:
package com.broadleafcommerce.customfield.spec
...
@ContextHierarchy([
@ContextConfiguration(name = "adminRoot", locations = [
"classpath:/bl-custom-field-admin-applicationContext.xml",
"classpath:/bl-custom-field-applicationContext.xml"])])
class CustomFieldAdminIntegrationSetup extends RootAdminIntegrationSetup {
}
Note that there is no code in the body of this class, and it does not specify a loader, or other annotations. Only a @ContextHierarchy
with a single @ContextConfiguration
that references the adminRoot
ContextConfiguration of the AdminIntegrationSetup
file, and includes the Contexts that the CustomField Module requires for admin Integration Tests. If we needed to create a setup file for Site based tests, we could call it CustomFieldSiteIntegrationSetup
, extend from the SiteIntegrationSetup
and reference the siteRoot
ContextConfiguration.
ITSpec
After we have created the IntegrationSetup
files, we can now create ITSpec
files that have actual test code inside them.
The class names of these specs must follow the format of either
*AdminITSpec
or*SiteITSpec
.
These classes extend from the setup file you created earlier for your project's Integration Tests, they have no class level annotations, and just include test code. As an example, here is an exerpt from CustomFieldAdminITSpec
package com.broadleafcommerce.customfield.spec
import ...
class CustomFieldAdminITSpec extends CustomFieldAdminIntegrationSetup {
@Resource
RuleBuilderFieldServiceFactory ruleBuilderFieldServiceFactory
...
The further contents of this file include normal groovy syntax based tests.
Testing RESTful Services
With this new integration test setup, we can now utilize the Spring-Test's MockMVC
api to test RESTful services. To do so, along with the above annotations, you will need to include a few new lines.
...
class testClassITSpec extends Specification {
@Resource
WebAppContext webAppContext
MockMvc mockMvc
def setup() {
mockMVC = MockMVCBuilders.webAppContextSetup(this.webAppContext).build()
}
...
These lines populate the WebAppContext
with the one created by Spring's @ContextConfiguration
annotation, declare our MockMVC
object, and then instantiate it with the WebAppContext
. Now, with it having been created, we can call restful services using MockMvc's only method, perform()
, such as:
def "Test RESTful service example"() {
...
mockMvc.perform(post("/restful/service/example")
.content(standardOrderJSON())
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isCreated())
mockMvc.perform(get("restful/service/example")
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].items['" + RestDataFixture.YUMMY_ITEM + "']").value(12))
...
}