Merge branch 'master' of github.com:Codearte/accurest

Conflicts:
	accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy
This commit is contained in:
Marcin Grzejszczak
2015-05-06 18:19:11 +02:00
52 changed files with 330 additions and 437 deletions

241
README.md
View File

@@ -1,239 +1,14 @@
Accurate REST
=============
[![Build Status](https://travis-ci.org/Codearte/accurest.svg?branch=master)](https://travis-ci.org/Codearte/accurest) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.codearte.accurest/accurest-gradle-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.codearte.accurest/accurest-gradle-plugin)
Consumer Driven Contracts verifier for Java
# Introduction
Just to make long story short - AccuREST is a tool for Consumer Driven Contract (CDC) development. AccuREST ships easy DSL for describing REST contracts for JVM-based applications. The contract DSL is used by AccuREST for two things:
We would like to use the Wiremock's JSON stub definitions as a point of entry to our approach of Consumer Driven Contracts (CDC). From these JSON stub definitions we would like to generate Acceptance tests that will allow you to start using CDC as TDD from architecture point of view.
generating Wiremock's JSON stub definitions, allowing rapid development of the consumer side,
generating Spock's acceptance tests for the server - to verify if your API implementation is compliant with the contract.
By using AccuREST you can move TDD to an architecture level.
# Notations
* __Collaborator__ - a service whom your service can contact
* __Client test__ - since your service is a client of your collaborator then the tests of your service are __client tests__
* __Server test__ - from a your service's (client) perspective your collaborator is a server that provides a functionality thus tests of your collaborators are __server tests__
# Why?
The main purpose of this approach is to:
- ensure that our stubs are doing exactly what the actual implementation does
- generate acceptance test cases from stub definitions (ATDD)
- make the stub definitions reusable
Below we depict the idea behind client and server side testing. Let's assume that for the sake
of this description that there is a _service X_ calling a _service Y_.
## Client side (service X)
During the tests you want to have a Wiremock instance up and running that simulates the service Y.
You would like to feed that instance with a proper stub definition. That stub definition would need
to be valid from the Wiremock's perspective but should also be reusable on the server side.
__Summing it up:__ On this side, in the stub definition, you can use patterns for request stubbing and you need exact
values for responses.
## Server side (service Y)
Being a service Y since you are developing your stub, you need to be sure that it's actually resembling your
concrete implementation. You can't have a situation where your stub acts in one way and your application on
production behaves in a different way.
That's why from the provided stub acceptance tests will be generated that will ensure
that your application behaves in the same way as you define in your stub.
__Summing it up:__ On this side, in the stub definition, you need exact values as request and can use patterns/methods
for response verification.
# Description
To achieve that we needed to tweak the standard Wiremock stub definitions by providing a possibility of entering
two values for one field. This is done via the following pattern:
```
${VALUE_FOR_CLIENT_TESTS:VALUE_FOR_SERVER_TESTS}
```
That means that depending on the need you can take either value for the client test or server test.
## Example
Let's take a look at the following example
```
{
"request": {
"method": "GET",
"urlPattern": "${/[0-9]{2}:/12}"
},
"response": {
"status": 200,
"body": "{\"date\":\"${\"2015-01-14\":$anyInt($it)}\"}}",
"headers": {
"Content-Type": "text/plain"
}
}
}
```
### Client side
From the client's perspective, in this particular scenario, you want a regexp matching a URL on which you send
a request (`[/0-9]{2}`) and a concrete value returned (`2015-01-14`). That way we will change your stub definition to:
```
{
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}"
},
"response": {
"status": 200,
"body": {
"date" : "2015-01-14"
},
"headers": {
"Content-Type": "text/plain"
}
}
}
```
### Server side
On the server side we need to generate acceptance tests. So we need to check that for given input (request)
we receive some matching output (response).
In this case we want to send a request to an endpoint `/12` and check if in the body of the response
we will receive the current date (`isCurrentDate(it.date)`).
The latter value will be checked if it's a method (if it contains parentheses) and then a method will be
called with the corresponding field's (status, body, headers etc.) value as input.
`VALUE_FOR_SERVER_TESTS` can use `$it` notation to pass body as method argument. The method in the generated Spec can be access also with `$` prefix - if you want to call a method `isPersonalIdValid(String requestBody)` you can do it as follows:
```
personaId : "${123456789:$isPersonalIdValid($it)}"
```
#### Example of generated specification
```
def responseBody = new JsonSlurper().parseText(response.body.asString())
isPersonalIdValid(responseBody.personaId)
```
### Full example
Below you can see sample stub and code of the test generated for this definition.
Stub definition:
```
{
"request": {
"method": "POST",
"url": "/loanApplication",
"headers": {
"Content-Type": {"equalTo": "application/vnd.loanapplicationservice.v1+json"}
},
"bodyPatterns": [{"matches": "\\{\"clientSsn\":\"1234567890\",\"loanAmount\":123.123\\}"}]
},
"response": {
"status": 200,
"body": "{\"loanApplicationStatus\":\"LOAN_APPLIED\",\"loanApplicationId\":\"${3245:$greaterThan($it, 1000)}\"}",
"headers": {"Content-Type": "application/vnd.loanapplicationservice.v1+json"}
}
}
```
and the code:
```
class AcceptanceSpec extends AssurestSpec {
def shouldApplyForLoan() {
given:
def request = given()
.header('Content-Type', 'application/vnd.loanapplicationservice.v1+json')
.body('{"clientSsn":"1234567890","loanAmount":123.123}')
when:
def response = given().spec(request)
.post("/loanApplication")
then:
response.statusCode == 200
response.header('Content-Type') == 'application/vnd.loanapplicationservice.v1+json'
def responseBody = new JsonSlurper().parseText(response.body.asString())
greaterThan(responseBody.loanApplicationId, 1000)
responseBody.loanApplicationStatus == "LOAN_APPLIED"
}
}
```
# Using in your project
## Add gradle plugin
```
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'io.codearte.accurest:accurest-gradle-plugin:0.4.4'
}
}
apply plugin: 'accurest'
dependecies {
testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
testCompile 'com.jayway.restassured:rest-assured:2.4.0'
testCompile 'com.jayway.restassured:spring-mock-mvc:2.4.0' // needed if you're going to use Spring MockMvc
}
```
## Add stubs
By default Accurest is looking for stubs in src/test/resources/stubs directory.
Directory containing stub definitions is treated as a class name, and each stub definition is treated as a single test.
We assume that it contains at least one directory which will be used as test class name. If there is more than one level of nested directories all except the last one will be used as package name.
So with following structure
src/test/resources/stubs/myservice/shouldCreateUser.json
src/test/resources/stubs/myservice/shouldReturnUser.json
Accurest will create test class `defaultBasePackage.MyService` with two methods
- shouldCreateUser()
- shouldReturnUser()
## Run plugin
Plugin registers itself to be invoked before `compileTestGroovy` task. You have nothing to do as long as you want it to be part of your build process. If you just want to generate tests please invoke `generateAccurest` task.
## Configure plugin
To change default configuration just add `accurest` snippet to your Gradle config
```
accurest {
testMode = 'MockMvc'
baseClassForTests = 'org.mycompany.tests'
generatedTestSourcesDir = 'src/accurest'
}
```
### Configuration options
- testMode - default 'MockMvc' uses Spring MockMvc to invoke tests. Can be changed to 'Direct' to support HTTP requests
- stubsBaseDirectory - where to look for stub definitions. Default 'src/test/resources/stubs'
- basePackageForTests - base package for test classes. Default 'io.codearte.accurest.tests'
- baseClassForTests - base class which will be extended by all generated tests. By default Accurest is using base framework class (for Spock it's `Specification`)
- ruleClassForTests - you can specify qualified name of rule which should be included in generated test
- generatedTestSourcesDir - target directory for generated tests. By default 'build/generated-sources/accurest'
- imports - array with imports that should be included in generated tests (for example ['org.myorg.Matchers']). By default empty array []
- staticImports - array with static imports that should be included in generated tests(for example ['org.myorg.Matchers.*']). By default empty array []
For more information please follow to the [Wiki](https://github.com/Codearte/accurest/wiki/1.-Introduction)

View File

@@ -1,7 +1,7 @@
package io.codearte.accurest.wiremock
import groovy.transform.CompileStatic
import io.coderate.accurest.AccurestException
import io.codearte.accurest.AccurestException
@CompileStatic
class ConversionAccurestException extends AccurestException {

View File

@@ -1,7 +1,7 @@
package io.codearte.accurest.wiremock
import groovy.transform.CompileStatic
import io.coderate.accurest.dsl.WiremockStubStrategy
import io.codearte.accurest.dsl.WiremockStubStrategy
@CompileStatic
class DslToWiremockClientConverter extends DslToWiremockConverter {

View File

@@ -1,7 +1,7 @@
package io.codearte.accurest.wiremock
import groovy.transform.CompileStatic
import io.coderate.accurest.dsl.GroovyDsl
import io.codearte.accurest.dsl.GroovyDsl
@CompileStatic
abstract class DslToWiremockConverter implements SingleFileConverter {

View File

@@ -3,6 +3,7 @@ import groovy.io.FileType
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.xml.XmlUtil
import io.codearte.accurest.dsl.GroovyDsl
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava
@@ -139,14 +140,23 @@ class WiremockToDslConverter {
if (!it.name.endsWith('json')) {
return
}
String wiremockStub = fromWiremockStub(it.text)
String dslFromWiremockStub = fromWiremockStub(it.text)
String dslWrappedWithFactoryMethod = wrapWithFactoryMethod(dslFromWiremockStub)
File newGroovyFile = new File(it.parent, it.name.replaceAll('json', 'groovy'))
println("Creating new groovy file [$newGroovyFile.path]")
newGroovyFile.text = wiremockStub
newGroovyFile.text = dslWrappedWithFactoryMethod
} catch (Exception e) {
System.err.println(e)
}
}
}
static String wrapWithFactoryMethod(String dslFromWiremockStub) {
return """\
${GroovyDsl.name}.make {
$dslFromWiremockStub
}
"""
}
}

View File

@@ -10,7 +10,7 @@ class DslToWiremockClientConverterSpec extends Specification {
def converter = new DslToWiremockClientConverter()
and:
String dslBody = """
io.coderate.accurest.dsl.GroovyDsl.make {
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method('PUT')
url \$(client(~/\\/[0-9]{2}/), server('/12'))
@@ -33,7 +33,7 @@ class DslToWiremockClientConverterSpec extends Specification {
def converter = new DslToWiremockClientConverter()
and:
String dslBody = """
io.coderate.accurest.dsl.GroovyDsl.make {
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method 'PUT'
url '/api/12'
@@ -84,9 +84,9 @@ class DslToWiremockClientConverterSpec extends Specification {
"request":{
"method":"PUT",
"url":"/api/12",
"bodyPatterns": {
"equalTo": "[{\\"created_at\\":\\"Sat Jul 26 09:38:57 +0000 2014\\",\\"id\\":492967299297845248,\\"id_str\\":\\"492967299297845248\\",\\"place\\":{\\"attributes\\":{},\\"bounding_box\\":{\\"coordinates\\":[[[-77.119759,38.791645],[-76.909393,38.791645],[-76.909393,38.995548],[-77.119759,38.995548]]],\\"type\\":\\"Polygon\\"},\\"country\\":\\"United States\\",\\"country_code\\":\\"US\\",\\"full_name\\":\\"Washington, DC\\",\\"id\\":\\"01fbe706f872cb32\\",\\"name\\":\\"Washington\\",\\"place_type\\":\\"city\\",\\"url\\":\\"http://api.twitter.com/1/geo/id/01fbe706f872cb32.json\\"},\\"text\\":\\"Gonna see you at Warsaw\\"}]"
},
"bodyPatterns": [
{ "equalTo": "[{\\"created_at\\":\\"Sat Jul 26 09:38:57 +0000 2014\\",\\"id\\":492967299297845248,\\"id_str\\":\\"492967299297845248\\",\\"place\\":{\\"attributes\\":{},\\"bounding_box\\":{\\"coordinates\\":[[[-77.119759,38.791645],[-76.909393,38.791645],[-76.909393,38.995548],[-77.119759,38.995548]]],\\"type\\":\\"Polygon\\"},\\"country\\":\\"United States\\",\\"country_code\\":\\"US\\",\\"full_name\\":\\"Washington, DC\\",\\"id\\":\\"01fbe706f872cb32\\",\\"name\\":\\"Washington\\",\\"place_type\\":\\"city\\",\\"url\\":\\"http://api.twitter.com/1/geo/id/01fbe706f872cb32.json\\"},\\"text\\":\\"Gonna see you at Warsaw\\"}]" }
],
"headers": {
"Content-Type": {
"equalTo": "application/vnd.com.ofg.twitter-places-analyzer.v1+json"

View File

@@ -1,6 +1,6 @@
package io.codearte.accurest.wiremock
import io.coderate.accurest.dsl.GroovyDsl
import io.codearte.accurest.dsl.GroovyDsl
import spock.lang.Specification
class WiremockToDslConverterSpec extends Specification {
@@ -23,14 +23,7 @@ class WiremockToDslConverterSpec extends Specification {
},
"response": {
"status": 200,
"body": {
"id": {
"value": "132"
},
"surname": "Kowalsky",
"name": "Jan",
"created" : "2014-02-02 12:23:43"
},
"body": "{ \\"id\\": { \\"value\\": \\"132\\" }, \\"surname\\": \\"Kowalsky\\", \\"name\\": \\"Jan\\", \\"created\\": \\"2014-02-02 12:23:43\\" }",
"headers": {
"Content-Type": "text/plain",
}
@@ -72,7 +65,7 @@ class WiremockToDslConverterSpec extends Specification {
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
@@ -124,7 +117,7 @@ class WiremockToDslConverterSpec extends Specification {
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
@@ -173,7 +166,7 @@ class WiremockToDslConverterSpec extends Specification {
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
@@ -193,11 +186,7 @@ class WiremockToDslConverterSpec extends Specification {
},
"response": {
"status": 200,
"body": [
{"a":1, "c":"3"},
"b",
"a"
],
"body": "[ {\\"a\\":1, \\"c\\":\\"3\\"}, \\"b\\", \\"a\\" ]",
"headers": {
"Content-Type": "application/json"
}
@@ -229,7 +218,7 @@ class WiremockToDslConverterSpec extends Specification {
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
@@ -293,7 +282,7 @@ class WiremockToDslConverterSpec extends Specification {
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}

View File

@@ -1,4 +1,4 @@
io.coderate.accurest.dsl.GroovyDsl.make {
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method('PUT')
headers {

View File

@@ -1,4 +1,4 @@
io.coderate.accurest.dsl.GroovyDsl.make {
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method('PUT')
headers {

View File

@@ -1,4 +1,4 @@
io.coderate.accurest.dsl.GroovyDsl.make {
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method('PUT')
headers {

View File

@@ -1,4 +1,4 @@
io.coderate.accurest.dsl.GroovyDsl.make {
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method('PUT')
headers {

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest
package io.codearte.accurest
/**
* @author Jakub Kubrynski

View File

@@ -1,16 +1,16 @@
package io.coderate.accurest
package io.codearte.accurest
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import io.coderate.accurest.config.TestFramework
import io.coderate.accurest.util.NamesUtil
import io.codearte.accurest.config.TestFramework
import io.codearte.accurest.util.NamesUtil
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardOpenOption
import static io.coderate.accurest.util.NamesUtil.capitalize
import static io.codearte.accurest.util.NamesUtil.capitalize
@CompileStatic
@Slf4j

View File

@@ -1,14 +1,14 @@
package io.coderate.accurest
package io.codearte.accurest
import groovy.transform.PackageScope
import io.coderate.accurest.builder.ClassBuilder
import io.coderate.accurest.config.AccurestConfigProperties
import io.coderate.accurest.config.TestFramework
import io.coderate.accurest.config.TestMode
import io.codearte.accurest.builder.ClassBuilder
import io.codearte.accurest.config.AccurestConfigProperties
import io.codearte.accurest.config.TestFramework
import io.codearte.accurest.config.TestMode
import static io.coderate.accurest.builder.ClassBuilder.createClass
import static io.coderate.accurest.builder.MethodBuilder.createTestMethod
import static io.coderate.accurest.util.NamesUtil.capitalize
import static io.codearte.accurest.builder.ClassBuilder.createClass
import static io.codearte.accurest.builder.MethodBuilder.createTestMethod
import static io.codearte.accurest.util.NamesUtil.capitalize
class SingleTestGenerator {
private final AccurestConfigProperties configProperties

View File

@@ -1,12 +1,12 @@
package io.coderate.accurest
package io.codearte.accurest
import groovy.transform.PackageScope
import io.coderate.accurest.config.AccurestConfigProperties
import io.codearte.accurest.config.AccurestConfigProperties
import org.apache.commons.io.FilenameUtils
import org.codehaus.plexus.util.DirectoryScanner
import java.util.concurrent.atomic.AtomicInteger
import static io.coderate.accurest.util.NamesUtil.afterLast
import static io.codearte.accurest.util.NamesUtil.afterLast
/**
* @author Jakub Kubrynski
*/

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.builder
package io.codearte.accurest.builder
import groovy.transform.PackageScope

View File

@@ -1,8 +1,8 @@
package io.coderate.accurest.builder
package io.codearte.accurest.builder
import io.coderate.accurest.config.AccurestConfigProperties
import io.coderate.accurest.config.TestFramework
import io.coderate.accurest.util.NamesUtil
import io.codearte.accurest.config.AccurestConfigProperties
import io.codearte.accurest.config.TestFramework
import io.codearte.accurest.util.NamesUtil
/**
* @author Jakub Kubrynski

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.builder
package io.codearte.accurest.builder
/**
* @author Jakub Kubrynski

View File

@@ -1,9 +1,9 @@
package io.coderate.accurest.builder
package io.codearte.accurest.builder
import groovy.util.logging.Slf4j
import io.coderate.accurest.config.TestFramework
import io.coderate.accurest.dsl.GroovyDsl
import io.coderate.accurest.util.NamesUtil
import io.codearte.accurest.config.TestFramework
import io.codearte.accurest.dsl.GroovyDsl
import io.codearte.accurest.util.NamesUtil
/**
* @author Jakub Kubrynski

View File

@@ -1,8 +1,9 @@
package io.coderate.accurest.builder
package io.codearte.accurest.builder
import groovy.json.JsonOutput
import groovy.transform.PackageScope
import io.coderate.accurest.dsl.GroovyDsl
import io.coderate.accurest.dsl.internal.Header
import io.codearte.accurest.dsl.GroovyDsl
import io.codearte.accurest.dsl.internal.Header
/**
* @author Jakub Kubrynski
@@ -46,8 +47,11 @@ class SpockMethodBodyBuilder {
blockBuilder.endBlock()
blockBuilder.addLine('and:').startBlock()
blockBuilder.addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())')
stubDefinition.response.body.serverValue.each {
processBodyElement(blockBuilder, "", it)
def responseBody = stubDefinition.response.body.serverValue
if (responseBody instanceof List) {
processArrayElements(responseBody, "", blockBuilder)
} else {
processMapElement(responseBody, blockBuilder, "")
}
}
blockBuilder.endBlock()
@@ -66,9 +70,24 @@ class SpockMethodBodyBuilder {
blockBuilder.addLine("responseBody$property == \"${value}\"")
}
} else if (value instanceof Map) {
value.each { entry -> processBodyElement(blockBuilder, property, entry) }
processMapElement(value, blockBuilder, property)
} else if (value instanceof List) {
processArrayElements(value, property, blockBuilder)
} else {
blockBuilder.addLine("responseBody$property == ${value}")
}
}
private void processMapElement(def value, BlockBuilder blockBuilder, String property) {
value.each { entry -> processBodyElement(blockBuilder, property, entry) }
}
private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) {
responseBody.eachWithIndex {
listElement, listIndex ->
listElement.each {
entry -> processBodyElement(blockBuilder, property + "[$listIndex]", entry)
}
}
}
}

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.config
package io.codearte.accurest.config
/**
* @author Jakub Kubrynski
*/

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.config
package io.codearte.accurest.config
/**
* @author Jakub Kubrynski

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.config
package io.codearte.accurest.config
/**
* @author Jakub Kubrynski

View File

@@ -0,0 +1,67 @@
package io.codearte.accurest.dsl
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.transform.TypeChecked
import groovy.xml.XmlUtil
import io.codearte.accurest.dsl.internal.Header
import io.codearte.accurest.dsl.internal.Headers
import java.util.regex.Pattern
import static groovy.json.StringEscapeUtils.escapeJava
@TypeChecked
abstract class BaseWiremockStubStrategy {
protected Map buildClientRequestHeadersSection(Headers headers) {
if (!headers) {
return null
}
return headers.entries.collectEntries { Header entry ->
parseHeader(entry.name, entry.clientValue)
}
}
protected Map buildClientResponseHeadersSection(Headers headers) {
if (!headers) {
return null
}
return headers.entries.collectEntries { Header entry ->
[(entry.name) : entry.clientValue]
}
}
protected Map parseHeader(String entryKey, Object entry) {
return [(entryKey): [equalTo : entry]]
}
protected Map parseHeader(String entryKey, String entry) {
return [(entryKey): [equalTo : entry]]
}
protected Map parseHeader(String entryKey, Pattern entry) {
return [(entryKey): [matches : entry.pattern()]]
}
protected String parseBody(Object body) {
String bodyAsString = body as String
try {
def json = new JsonSlurper().parseText(bodyAsString)
return escapeJava(JsonOutput.toJson(bodyAsString))
} catch (Exception jsonException) {
try {
def xml = new XmlSlurper().parseText(bodyAsString)
return escapeJava(XmlUtil.serialize(bodyAsString))
} catch (Exception xmlException) {
return escapeJava(bodyAsString)
}
}
}
protected String parseBody(List body) {
return JsonOutput.toJson(body)
}
protected String parseBody(Map body) {
return JsonOutput.toJson(body)
}
}

View File

@@ -1,10 +1,10 @@
package io.coderate.accurest.dsl
package io.codearte.accurest.dsl
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.transform.TypeChecked
import io.coderate.accurest.dsl.internal.Request
import io.coderate.accurest.dsl.internal.Response
import io.codearte.accurest.dsl.internal.Request
import io.codearte.accurest.dsl.internal.Response
@TypeChecked
@EqualsAndHashCode(includeFields = true)

View File

@@ -1,11 +1,9 @@
package io.coderate.accurest.dsl
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
package io.codearte.accurest.dsl
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import groovy.xml.XmlUtil
import io.coderate.accurest.dsl.internal.ClientRequest
import io.coderate.accurest.dsl.internal.Request
import io.codearte.accurest.dsl.internal.ClientRequest
import io.codearte.accurest.dsl.internal.Request
import java.util.regex.Pattern
@@ -54,27 +52,4 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy {
return (bodyString =~ /\^.*\$/).find()
}
private String parseBody(Object bodyObject) {
String responseBody = bodyObject as String
try {
def json = new JsonSlurper().parseText(responseBody)
return escapeJava(JsonOutput.toJson(responseBody))
} catch (Exception jsonException) {
try {
def xml = new XmlSlurper().parseText(responseBody)
return escapeJava(XmlUtil.serialize(responseBody))
} catch (Exception xmlException) {
return escapeJava(responseBody)
}
}
}
private String parseBody(List responseBody) {
return JsonOutput.toJson(responseBody)
}
private String parseBody(Map responseBody) {
return JsonOutput.toJson(responseBody)
}
}

View File

@@ -1,10 +1,10 @@
package io.coderate.accurest.dsl
import groovy.transform.CompileStatic
package io.codearte.accurest.dsl
import groovy.transform.PackageScope
import io.coderate.accurest.dsl.internal.ClientResponse
import io.coderate.accurest.dsl.internal.Response
import groovy.transform.TypeChecked
import io.codearte.accurest.dsl.internal.ClientResponse
import io.codearte.accurest.dsl.internal.Response
@CompileStatic
@TypeChecked
@PackageScope
class WiremockResponseStubStrategy extends BaseWiremockStubStrategy {
@@ -20,8 +20,13 @@ class WiremockResponseStubStrategy extends BaseWiremockStubStrategy {
}
private Map<String, Object> buildResponseContent(ClientResponse response) {
return [status : response?.status?.clientValue,
body : response?.body?.clientValue,
headers: buildClientResponseHeadersSection(response.headers)].findAll { it.value }
return ([status : response?.status?.clientValue,
headers: buildClientResponseHeadersSection(response.headers)
] << appendBody(response)).findAll { it.value }
}
private Map<String, Object> appendBody(ClientResponse response) {
Object body = response?.body?.clientValue
return body != null ? [body: parseBody(body)] : [:]
}
}

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl
package io.codearte.accurest.dsl
import groovy.json.JsonOutput
import groovy.transform.CompileStatic

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.json.JsonSlurper
import groovy.transform.EqualsAndHashCode

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.transform.PackageScope
import groovy.transform.TypeChecked

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
enum JSONCompareMode {
STRICT, LENIENT, NON_EXTENSIBLE, STRICT_ORDER

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.dsl.internal
package io.codearte.accurest.dsl.internal
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.util
package io.codearte.accurest.util
/**
* @author Jakub Kubrynski

View File

@@ -1,4 +1,4 @@
package io.coderate.accurest.util
package io.codearte.accurest.util
import groovy.json.JsonException
import groovy.json.JsonSlurper

View File

@@ -1,41 +0,0 @@
package io.coderate.accurest.dsl
import groovy.transform.TypeChecked
import io.coderate.accurest.dsl.internal.Header
import io.coderate.accurest.dsl.internal.Headers
import java.util.regex.Pattern
@TypeChecked
abstract class BaseWiremockStubStrategy {
protected Map buildClientRequestHeadersSection(Headers headers) {
if (!headers) {
return null
}
return headers.entries.collectEntries { Header entry ->
parseHeader(entry.name, entry.clientValue)
}
}
protected Map buildClientResponseHeadersSection(Headers headers) {
if (!headers) {
return null
}
return headers.entries.collectEntries { Header entry ->
[(entry.name) : entry.clientValue]
}
}
protected Map parseHeader(String entryKey, Object entry) {
return [(entryKey): [equalTo : entry]]
}
protected Map parseHeader(String entryKey, String entry) {
return [(entryKey): [equalTo : entry]]
}
protected Map parseHeader(String entryKey, Pattern entry) {
return [(entryKey): [matches : entry.pattern()]]
}
}

View File

@@ -1,12 +1,9 @@
package io.codearte.accurest
import io.coderate.accurest.FileSaver
import io.coderate.accurest.SingleTestGenerator
import io.coderate.accurest.TestGenerator
import io.coderate.accurest.config.AccurestConfigProperties
import io.codearte.accurest.config.AccurestConfigProperties
import spock.lang.Specification
class TestGeneratorSpec extends Specification {
class GeneratorScannerSpec extends Specification {
private SingleTestGenerator classGenerator = Mock(SingleTestGenerator)

View File

@@ -1,9 +1,8 @@
package io.codearte.accurest
import io.coderate.accurest.TestGenerator
import io.coderate.accurest.config.AccurestConfigProperties
import io.coderate.accurest.config.TestFramework
import io.coderate.accurest.config.TestMode
import io.codearte.accurest.config.AccurestConfigProperties
import io.codearte.accurest.config.TestFramework
import io.codearte.accurest.config.TestMode
class MainTest {
public static void main(String[] args) {

View File

@@ -0,0 +1,113 @@
package io.codearte.accurest.builder
import io.codearte.accurest.dsl.GroovyDsl
import spock.lang.Specification
/**
* @author Jakub Kubrynski
*/
class SpockMethodBuilderSpec extends Specification {
def "should generate assertions for simple response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """{
"property1": "a",
"property2": "b"
}"""
}
}
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("responseBody.property1 == \"a\"")
blockBuilder.toString().contains("responseBody.property2 == \"b\"")
}
def "should generate assertions for array in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """[
{
"property1": "a"
},
{
"property2": "b"
}]"""
}
}
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("responseBody[0].property1 == \"a\"")
blockBuilder.toString().contains("responseBody[1].property2 == \"b\"")
}
def "should generate assertions for array inside response body element"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """{
"property1": [
{ "property2": "test1"},
{ "property3": "test2"}
]
}"""
}
}
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"")
blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"")
}
def "should generate assertions for nested objects in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body '''\
{
"property1": "a",
"property2": {"property3": "b"}
}
'''
}
}
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("responseBody.property1 == \"a\"")
blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"")
}
}

View File

@@ -1,8 +1,6 @@
package io.codearte.accurest.dsl
import groovy.json.JsonSlurper
import io.coderate.accurest.dsl.GroovyDsl
import io.coderate.accurest.dsl.WiremockResponseStubStrategy
import spock.lang.Specification
class WiremockGroovyDslResponseSpec extends Specification {

View File

@@ -1,8 +1,5 @@
package io.codearte.accurest.dsl
import groovy.json.JsonSlurper
import io.coderate.accurest.dsl.GroovyDsl
import io.coderate.accurest.dsl.WiremockRequestStubStrategy
import io.coderate.accurest.dsl.WiremockStubStrategy
import spock.lang.Specification
class WiremockGroovyDslSpec extends Specification {
@@ -44,12 +41,7 @@ class WiremockGroovyDslSpec extends Specification {
},
"response": {
"status": 200,
"body": {
"id": "123",
"surname": "Kowalsky",
"name": "Jan",
"created" : "2014-02-02 12:23:43"
},
"body": "{\\"id\\":\\"123\\",\\"surname\\":\\"Kowalsky\\",\\"name\\":\\"Jan\\",\\"created\\":\\"2014-02-02 12:23:43\\"}",
"headers": {
"Content-Type": "text/plain"
}
@@ -92,12 +84,7 @@ class WiremockGroovyDslSpec extends Specification {
},
"response": {
"status": 200,
"body": {
"id": "123",
"surname": "Kowalsky",
"name": "Jan",
"created" : "2014-02-02 12:23:43"
},
"body": "{\\"created\\":\\"2014-02-02 12:23:43\\",\\"id\\":\\"123\\",\\"name\\":\\"Jan\\",\\"surname\\":\\"Kowalsky\\"}",
"headers": {
"Content-Type": "text/plain"
}
@@ -139,15 +126,15 @@ class WiremockGroovyDslSpec extends Specification {
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}",
"bodyPatterns": {
"equalTo":"{\\"name\\":\\"Jan\\"}"
}
"bodyPatterns": [
{
"equalTo":"{\\"name\\":\\"Jan\\"}"
}
]
},
"response": {
"status": 200,
"body": {
"name": "Jan"
},
"body": "{\\"name\\":\\"Jan\\"}",
"headers": {
"Content-Type": "text/plain"
}

View File

@@ -1,4 +1,4 @@
io.coderate.accurest.dsl.GroovyDsl.make {
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method('PUT')
headers {

View File

@@ -1,6 +1,6 @@
package io.codearte.accurest.plugin
import io.coderate.accurest.config.AccurestConfigProperties
import io.codearte.accurest.config.AccurestConfigProperties
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task

View File

@@ -1,8 +1,8 @@
package io.codearte.accurest.plugin
import io.coderate.accurest.AccurestException
import io.coderate.accurest.TestGenerator
import io.coderate.accurest.config.AccurestConfigProperties
import io.codearte.accurest.AccurestException
import io.codearte.accurest.TestGenerator
import io.codearte.accurest.config.AccurestConfigProperties
import org.gradle.api.GradleException
import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.InputDirectory

View File

@@ -51,9 +51,9 @@ class BasicFunctionalSpec extends IntegrationSpec {
}
},
"url": "/api/12",
"bodyPatterns": {
"equalTo": "[{\\"text\\":\\"Gonna see you at Warsaw\\"}]"
}
"bodyPatterns": [
{ "equalTo": "[{\\"text\\":\\"Gonna see you at Warsaw\\"}]" }
]
},
"response": {
"status": 200

View File

@@ -1,4 +1,4 @@
io.coderate.accurest.dsl.GroovyDsl.make {
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method 'PUT'
url '/api/12'