From cb803320cdd688b119e7fc4cc65f14bef73c2ada Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 28 Dec 2016 19:38:12 +0100 Subject: [PATCH] Updated tests for the context path without this change the explicit mode is broken and context path scenario can't be tested with this change you can provide the EXPLICIT mode with which you can set the RestAssured to send real requests. That way you can set up your application to listen on a socket and have the context path setup. The contracts need to include the context path too since in real world you'll send a request to a URL that contains a context path element. fixes #179 #117 --- docs/src/main/asciidoc/verifier/contract.adoc | 53 ++++++ .../ExplicitJUnitMethodBodyBuilder.groovy | 49 ++++++ .../builder/JUnitMethodBodyBuilder.groovy | 12 +- .../verifier/builder/MethodBuilder.groovy | 19 ++- .../MockMvcJUnitMethodBodyBuilder.groovy | 49 +----- .../RestAssuredJUnitMethodBodyBuilder.groovy | 89 ++++++++++ .../builder/SingleTestGenerator.groovy | 3 + .../builder/SingleTestGeneratorSpec.groovy | 154 ++++++++++++++---- .../verifier/util/SyntaxChecker.groovy | 20 ++- .../loan/LoanApplicationServiceTests.java | 17 +- ...udDetectionServer-0.0.1-SNAPSHOT-stubs.jar | Bin 0 -> 1916 bytes ...thFraudDetectionServer-0.0.1-SNAPSHOT.pom} | 2 +- .../maven-metadata.xml | 2 +- ...udDetectionServer-0.0.1-SNAPSHOT-stubs.jar | Bin 1897 -> 0 bytes 14 files changed, 377 insertions(+), 92 deletions(-) create mode 100644 spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/ExplicitJUnitMethodBodyBuilder.groovy create mode 100644 spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy create mode 100644 tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/contextPathFraudDetectionServer/0.0.1-SNAPSHOT/contextPathFraudDetectionServer-0.0.1-SNAPSHOT-stubs.jar rename tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/{fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT.pom => contextPathFraudDetectionServer/0.0.1-SNAPSHOT/contextPathFraudDetectionServer-0.0.1-SNAPSHOT.pom} (94%) rename tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/{fraudDetectionServer => contextPathFraudDetectionServer}/maven-metadata.xml (94%) delete mode 100644 tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT-stubs.jar diff --git a/docs/src/main/asciidoc/verifier/contract.adoc b/docs/src/main/asciidoc/verifier/contract.adoc index 4aece20bef..3733ad09f5 100644 --- a/docs/src/main/asciidoc/verifier/contract.adoc +++ b/docs/src/main/asciidoc/verifier/contract.adoc @@ -279,6 +279,59 @@ org.springframework.cloud.contract.spec.Contract.make { } ---- +==== Working with Context Paths + +Spring Cloud Contract supports context paths. + +IMPORTANT: The only thing that changes in order to fully support context paths is the switch +on the *PRODUCER* side. The autogenerated tests need to be using the *EXPLICIT* mode. + +The consumer side remains untouched, in order for the generated test to pass you have to switch the *EXPLICIT* mode. + +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + org.springframework.cloud + spring-cloud-contract-maven-plugin + ${spring-cloud-contract.version} + true + + EXPLICIT + + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +contracts { + testMode = 'EXPLICIT' +} +---- + +That way you'll generate a test that *DOES NOT* use MockMvc. It means that you're generating +real requests and you need to setup your generated test's base class to work on a real socket. + +Let's imagine the following contract: + +[source,groovy,indent=0] +---- +include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy[tags=context_path_contract,indent=0] +---- + +Here is an example of how to set up a base class and Rest Assured for everything to work correctly. + +[source,groovy,indent=0] +---- +include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy[tags=context_path_baseclass,indent=0] +---- + +That way all: + +- all your requests in the autogenerated tests will be sent to the real endpoint with your context path included (e.g. `/my-context-path/url`) +- your contracts reflect that you have a context path, thus your generated stubs will also + have that information (e.g. in the stubs you'll see that you have too call `/my-context-path/url`) ==== Messaging Top-Level Elements diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/ExplicitJUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/ExplicitJUnitMethodBodyBuilder.groovy new file mode 100644 index 0000000000..09baadc1fd --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/ExplicitJUnitMethodBodyBuilder.groovy @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.verifier.builder + +import groovy.transform.PackageScope +import groovy.transform.TypeChecked +import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties + +/** + * A {@link JUnitMethodBodyBuilder} implementation that uses Rest Assured in explicit mode + * + * @author Marcin Grzejszczak + * + * @since 1.0.3 + */ +@TypeChecked +@PackageScope +class ExplicitJUnitMethodBodyBuilder extends RestAssuredJUnitMethodBodyBuilder { + + ExplicitJUnitMethodBodyBuilder(Contract stubDefinition, ContractVerifierConfigProperties configProperties) { + super(stubDefinition, configProperties) + } + + @Override + protected String returnedResponseType() { + return "Response" + } + + @Override + protected String returnedRequestType() { + return "RequestSpecification" + } + +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy index fcdc08057a..a41ec6d2e9 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy @@ -126,16 +126,24 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder @Override protected String getInputString(Request request) { - def inputString = 'ResponseOptions response = given().spec(request)' + def inputString = "${returnedResponseType()} response = given().spec(request)" if (response.async){ inputString = inputString + '.when().async()' } return inputString } + protected String returnedResponseType() { + return "ResponseOptions" + } + @Override protected String getInputString() { - return 'MockMvcRequestSpecification request = given()' + return "${returnedRequestType()} request = given()" + } + + protected String returnedRequestType() { + return "MockMvcRequestSpecification" } @Override diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy index 60927dde38..eacb50f2b3 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy @@ -22,9 +22,9 @@ import groovy.util.logging.Slf4j import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties import org.springframework.cloud.contract.verifier.config.TestFramework -import org.springframework.cloud.contract.verifier.util.NamesUtil import org.springframework.cloud.contract.verifier.config.TestMode import org.springframework.cloud.contract.verifier.file.ContractMetadata +import org.springframework.cloud.contract.verifier.util.NamesUtil /** * Builds a test method. Adds an ignore annotation on a method if necessary. @@ -78,21 +78,26 @@ class MethodBuilder { private MethodBodyBuilder getMethodBodyBuilder() { if (stubContent.input || stubContent.outputMessage) { - if (configProperties.targetFramework == TestFramework.JUNIT){ + if (configProperties.targetFramework == TestFramework.JUNIT) { return new JUnitMessagingMethodBodyBuilder(stubContent, configProperties) } return new SpockMessagingMethodBodyBuilder(stubContent, configProperties) } - if (configProperties.testMode == TestMode.MOCKMVC && configProperties.targetFramework == TestFramework.JUNIT){ - return new MockMvcJUnitMethodBodyBuilder(stubContent, configProperties) - } if (configProperties.testMode == TestMode.JAXRSCLIENT) { - if (configProperties.targetFramework == TestFramework.JUNIT){ + if (configProperties.targetFramework == TestFramework.JUNIT) { return new JaxRsClientJUnitMethodBodyBuilder(stubContent, configProperties) } return new JaxRsClientSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties) + } else if (configProperties.testMode == TestMode.EXPLICIT) { + if (configProperties.targetFramework == TestFramework.JUNIT) { + return new ExplicitJUnitMethodBodyBuilder(stubContent, configProperties) + } + // in Groovy we're using def so we don't have to update the imports + return new MockMvcSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties) + } else if (configProperties.targetFramework == TestFramework.SPOCK) { + return new MockMvcSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties) } - return new MockMvcSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties) + return new MockMvcJUnitMethodBodyBuilder(stubContent, configProperties) } } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcJUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcJUnitMethodBodyBuilder.groovy index f9bdd6f6cb..ef9d17ffd7 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcJUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcJUnitMethodBodyBuilder.groovy @@ -19,12 +19,8 @@ package org.springframework.cloud.contract.verifier.builder import groovy.transform.PackageScope import groovy.transform.TypeChecked import org.springframework.cloud.contract.spec.Contract -import org.springframework.cloud.contract.spec.internal.ExecutionProperty -import org.springframework.cloud.contract.spec.internal.Header -import org.springframework.cloud.contract.spec.internal.NotToEscapePattern import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties -import java.util.regex.Pattern /** * A {@link JUnitMethodBodyBuilder} implementation that uses MockMvc to send requests. * @@ -34,55 +30,20 @@ import java.util.regex.Pattern */ @TypeChecked @PackageScope -class MockMvcJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { +class MockMvcJUnitMethodBodyBuilder extends RestAssuredJUnitMethodBodyBuilder { MockMvcJUnitMethodBodyBuilder(Contract stubDefinition, ContractVerifierConfigProperties configProperties) { super(stubDefinition, configProperties) } @Override - protected void validateResponseCodeBlock(BlockBuilder bb) { - bb.addLine("assertThat(response.statusCode()).isEqualTo($response.status.serverValue);") + protected String returnedResponseType() { + return "ResponseOptions" } @Override - protected void validateResponseHeadersBlock(BlockBuilder bb) { - response.headers?.executeForEachHeader { Header header ->\ - processHeaderElement(bb, header.name, header.serverValue) - } - } - - @Override - protected String getResponseBodyPropertyComparisonString(String property, Object value) { - return null - } - - @Override - protected String getResponseBodyPropertyComparisonString(String property, Pattern value) { - return null - } - - @Override - protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) { - if (value instanceof NotToEscapePattern) { - blockBuilder.addLine("assertThat(response.header(\"$property\"))." + - "${createMatchesMethod(value.serverValue.pattern().replace("\\", "\\\\"))};") - } - } - - @Override - protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) { - blockBuilder.addLine("assertThat(response.header(\"$property\")).${createHeaderComparison(value)}") - } - - @Override - protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern pattern) { - blockBuilder.addLine("assertThat(response.header(\"$property\")).${createHeaderComparison(pattern)}") - } - - @Override - protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { - blockBuilder.addLine("${exec.insertValue("response.header(\"$property\")")};") + protected String returnedRequestType() { + return "MockMvcRequestSpecification" } } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy new file mode 100644 index 0000000000..52e6741f88 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy @@ -0,0 +1,89 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.verifier.builder + +import groovy.transform.PackageScope +import groovy.transform.TypeChecked +import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.spec.internal.ExecutionProperty +import org.springframework.cloud.contract.spec.internal.Header +import org.springframework.cloud.contract.spec.internal.NotToEscapePattern +import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties + +import java.util.regex.Pattern + +/** + * A {@link JUnitMethodBodyBuilder} implementation that uses Rest Assured. + * + * @author Marcin Grzejszczak + * + * @since 1.0.3 + */ +@TypeChecked +@PackageScope +class RestAssuredJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { + + RestAssuredJUnitMethodBodyBuilder(Contract stubDefinition, ContractVerifierConfigProperties configProperties) { + super(stubDefinition, configProperties) + } + + @Override + protected void validateResponseCodeBlock(BlockBuilder bb) { + bb.addLine("assertThat(response.statusCode()).isEqualTo($response.status.serverValue);") + } + + @Override + protected void validateResponseHeadersBlock(BlockBuilder bb) { + response.headers?.executeForEachHeader { Header header ->\ + processHeaderElement(bb, header.name, header.serverValue) + } + } + + @Override + protected String getResponseBodyPropertyComparisonString(String property, Object value) { + return null + } + + @Override + protected String getResponseBodyPropertyComparisonString(String property, Pattern value) { + return null + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) { + if (value instanceof NotToEscapePattern) { + blockBuilder.addLine("assertThat(response.header(\"$property\"))." + + "${createMatchesMethod(value.serverValue.pattern().replace("\\", "\\\\"))};") + } + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) { + blockBuilder.addLine("assertThat(response.header(\"$property\")).${createHeaderComparison(value)}") + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern pattern) { + blockBuilder.addLine("assertThat(response.header(\"$property\")).${createHeaderComparison(pattern)}") + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { + blockBuilder.addLine("${exec.insertValue("response.header(\"$property\")")};") + } + +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy index 0109024833..3b051848f0 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy @@ -94,6 +94,9 @@ class SingleTestGenerator { if (contracts.values().contains(TestType.HTTP) && configProperties.testMode == TestMode.MOCKMVC) { clazz.addImport('com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification') clazz.addImport('com.jayway.restassured.response.ResponseOptions') + } else if (contracts.values().contains(TestType.HTTP) && configProperties.testMode == TestMode.EXPLICIT) { + clazz.addImport('com.jayway.restassured.specification.RequestSpecification') + clazz.addImport('com.jayway.restassured.response.Response') } clazz.addImport('org.junit.Test') clazz.addStaticImport('org.assertj.core.api.Assertions.assertThat') diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy index 8db73be660..5fa08b4f7e 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy @@ -1,7 +1,7 @@ /* * Copyright 2013-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License") * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * @@ -21,6 +21,7 @@ import org.junit.rules.TemporaryFolder import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties import org.springframework.cloud.contract.verifier.config.TestMode import org.springframework.cloud.contract.verifier.file.ContractMetadata +import org.springframework.cloud.contract.verifier.util.SyntaxChecker import spock.lang.Issue import spock.lang.Specification @@ -33,16 +34,44 @@ class SingleTestGeneratorSpec extends Specification { TemporaryFolder tmpFolder = new TemporaryFolder() File file - static List jUnitClassStrings = ['package test;', 'import com.jayway.jsonpath.DocumentContext;', 'import com.jayway.jsonpath.JsonPath;', - 'import org.junit.FixMethodOrder;', 'import org.junit.Ignore;', 'import org.junit.Test;', 'import org.junit.runners.MethodSorters;', - 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;', - '@FixMethodOrder(MethodSorters.NAME_ASCENDING)', '@Test', '@Ignore', 'mport com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;', - 'import com.jayway.restassured.response.ResponseOptions;', 'import static org.assertj.core.api.Assertions.assertThat;'] + private static final List mockMvcJUnitClassStrings = ['package test; ', 'import com.jayway.jsonpath.DocumentContext; ', 'import com.jayway.jsonpath.JsonPath; ', + 'import org.junit.FixMethodOrder; ', 'import org.junit.Ignore; ', 'import org.junit.Test; ', 'import org.junit.runners.MethodSorters; ', + 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson; ', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*; ', + '@FixMethodOrder(MethodSorters.NAME_ASCENDING); ', '@Test; ', '@Ignore; ', 'import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification; ', + 'import com.jayway.restassured.response.ResponseOptions; ', 'import static org.assertj.core.api.Assertions.assertThat'] - static List spockClassStrings = ['package test', 'import com.jayway.jsonpath.DocumentContext', 'import com.jayway.jsonpath.JsonPath', - 'import spock.lang.Ignore', 'import spock.lang.Specification', 'import spock.lang.Stepwise', - 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*', - '@Stepwise', '@Ignore'] + + private static final List explicitJUnitClassStrings = ['package test; ', 'import com.jayway.jsonpath.DocumentContext; ', 'import com.jayway.jsonpath.JsonPath; ', + 'import org.junit.FixMethodOrder; ', 'import org.junit.Ignore; ', 'import org.junit.Test; ', 'import org.junit.runners.MethodSorters; ', + 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson; ', 'import static com.jayway.restassured.RestAssured.*; ', + '@FixMethodOrder(MethodSorters.NAME_ASCENDING); ', '@Test; ', '@Ignore; ', 'import com.jayway.restassured.specification.RequestSpecification; ', + 'import com.jayway.restassured.response.Response; ', 'import static org.assertj.core.api.Assertions.assertThat'] + + private static final List spockClassStrings = ['package test', 'import com.jayway.jsonpath.DocumentContext', 'import com.jayway.jsonpath.JsonPath', + 'import spock.lang.Ignore', 'import spock.lang.Specification', 'import spock.lang.Stepwise', + 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson', 'import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*', + '@Stepwise', '@Ignore'] + + private static final List explicitSpockClassStrings = ['package test', 'import com.jayway.jsonpath.DocumentContext', 'import com.jayway.jsonpath.JsonPath', + 'import spock.lang.Ignore', 'import spock.lang.Specification', 'import spock.lang.Stepwise', + 'import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson', 'import static com.jayway.restassured.RestAssured.*', + '@Stepwise', '@Ignore'] + + public static final Closure JAVA_ASSERTER = { String classToTest -> + String name = Math.abs(new Random().nextInt()) + String changedTest = classToTest.replace("public class Test", "public class Test${name}") + SyntaxChecker.tryToCompileJavaWithoutImports("test.Test${name}", changedTest) + } + + public static final Closure JAVA_JAXRS_ASSERTER = { String classToTest -> + String name = Math.abs(new Random().nextInt()) + String changedTest = classToTest.replace("public class Test {", "public class Test${name} {\njavax.ws.rs.client.WebTarget webTarget;\n") + SyntaxChecker.tryToCompileJavaWithoutImports("test.Test${name}", changedTest) + } + + public static final Closure GROOVY_ASSERTER = { String classToTest -> + SyntaxChecker.tryToCompileGroovyWithoutImports(classToTest) + } def setup() { file = tmpFolder.newFile() @@ -59,9 +88,9 @@ class SingleTestGeneratorSpec extends Specification { """) } - def "should build MockMvc test class for #testFramework"() { + def "should build test class for #testFramework"() { given: - ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(); + ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = testFramework ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2) contract.ignored >> true @@ -73,16 +102,19 @@ class SingleTestGeneratorSpec extends Specification { then: classStrings.each { clazz.contains(it) } - + and: + asserter(clazz) where: - testFramework | classStrings - JUNIT | jUnitClassStrings - SPOCK | spockClassStrings + testFramework | mode | classStrings | asserter + JUNIT | TestMode.MOCKMVC | mockMvcJUnitClassStrings | JAVA_ASSERTER + JUNIT | TestMode.EXPLICIT | explicitJUnitClassStrings | JAVA_ASSERTER + SPOCK | TestMode.MOCKMVC | spockClassStrings | GROOVY_ASSERTER + SPOCK | TestMode.EXPLICIT | explicitSpockClassStrings | GROOVY_ASSERTER } def "should build JaxRs test class for #testFramework"() { given: - ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(); + ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.testMode = TestMode.JAXRSCLIENT properties.targetFramework = testFramework ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2) @@ -96,10 +128,13 @@ class SingleTestGeneratorSpec extends Specification { then: classStrings.each { clazz.contains(it) } + and: + asserter(clazz) + where: - testFramework | classStrings - JUNIT | ['import static javax.ws.rs.client.Entity.*;', 'import javax.ws.rs.core.Response;'] - SPOCK | ['import static javax.ws.rs.client.Entity.*;'] + testFramework | classStrings | asserter + JUNIT | ['import static javax.ws.rs.client.Entity.*', 'import javax.ws.rs.core.Response'] | JAVA_JAXRS_ASSERTER + SPOCK | ['import static javax.ws.rs.client.Entity.*'] | GROOVY_ASSERTER } def "should work if there is messaging and rest in one folder #testFramework"() { @@ -116,12 +151,12 @@ class SingleTestGeneratorSpec extends Specification { messageHeaders { header('sample', 'header') } - assertThat('bookWasDeleted()') + assertThat('hashCode()') } } """) and: - ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(); + ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = testFramework ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2) contract.ignored >> true @@ -140,10 +175,13 @@ class SingleTestGeneratorSpec extends Specification { classStrings.each { clazz.contains(it) } clazz.contains('@Inject ContractVerifierMessaging') + and: + asserter(clazz) + where: - testFramework | classStrings - JUNIT | jUnitClassStrings - SPOCK | spockClassStrings + testFramework | classStrings | asserter + JUNIT | mockMvcJUnitClassStrings | JAVA_ASSERTER + SPOCK | spockClassStrings | GROOVY_ASSERTER } @Issue('#30') @@ -163,7 +201,7 @@ class SingleTestGeneratorSpec extends Specification { } """) and: - ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(); + ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() properties.targetFramework = testFramework and: ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, 2) @@ -179,10 +217,68 @@ class SingleTestGeneratorSpec extends Specification { classStrings.each { clazz.contains(it) } clazz.contains('@Ignore') + and: + asserter(clazz) + where: - testFramework | classStrings - JUNIT | jUnitClassStrings - SPOCK | spockClassStrings + testFramework | classStrings | asserter + JUNIT | mockMvcJUnitClassStrings | JAVA_ASSERTER + SPOCK | spockClassStrings | GROOVY_ASSERTER + } + + @Issue('#117') + def "should generate test in explicit test mode using JUnit"() { + given: + String baseClass = """ + // tag::context_path_baseclass[] + import com.jayway.restassured.RestAssured; + import org.junit.Before; + import org.springframework.boot.context.embedded.LocalServerPort; + import org.springframework.boot.test.context.SpringBootTest; + + @SpringBootTest(classes = ContextPathTestingBaseClass.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) + class ContextPathTestingBaseClass { + + @LocalServerPort int port; + + @Before + public void setup() { + RestAssured.baseURI = "http://localhost"; + RestAssured.port = this.port; + } + } + // end::context_path_baseclass[] + """ + SyntaxChecker.tryToCompileJavaWithoutImports("test.ContextPathTestingBaseClass", "package test;\n${baseClass}") + and: + File secondFile = tmpFolder.newFile() + secondFile.write(""" + // tag::context_path_contract[] + org.springframework.cloud.contract.spec.Contract.make { + request { + method 'GET' + url '/my-context-path/url' + } + response { + status 200 + } + } + // end::context_path_contract[] + """) + and: + ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() + properties.targetFramework = JUNIT + properties.testMode = TestMode.EXPLICIT + properties.baseClassForTests = "test.ContextPathTestingBaseClass" + and: + ContractMetadata contract = new ContractMetadata(file.toPath(), false, 1, null) + and: + SingleTestGenerator testGenerator = new SingleTestGenerator(properties) + when: + String clazz = testGenerator.buildClass([contract], "test", "test", 'com/foo') + then: + clazz.contains("RequestSpecification request = given();") + clazz.contains("Response response = given().spec(request)") } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy index e4f0d39f16..07493e1b37 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy @@ -43,7 +43,7 @@ class SyntaxChecker { ].collect { "import static ${it};"}.join("\n") - public static void tryToCompile(String builderName, String test) { + static void tryToCompile(String builderName, String test) { if (builderName.toLowerCase().contains("spock")) { tryToCompileGroovy(test) } else { @@ -51,7 +51,7 @@ class SyntaxChecker { } } - public static void tryToCompileGroovy(String test) { + static void tryToCompileGroovy(String test) { def imports = new ImportCustomizer() CompilerConfiguration configuration = new CompilerConfiguration() configuration.addCompilationCustomizers(imports) @@ -65,7 +65,7 @@ class SyntaxChecker { new GroovyShell(SyntaxChecker.classLoader, configuration).parse(sourceCode.toString()) } - public static Class tryToCompileJava(String test) { + static Class tryToCompileJava(String test) { Random random = new Random() int first = Math.abs(random.nextInt()) int hashCode = Math.abs(test.hashCode()) @@ -76,15 +76,25 @@ class SyntaxChecker { sourceCode.append("${DEFAULT_IMPORTS_AS_STRING}\n") sourceCode.append("${STATIC_IMPORTS}\n") sourceCode.append("\n") - sourceCode.append("public class ${className} {\n") + sourceCode.append("class ${className} {\n") sourceCode.append("\n") sourceCode.append(" WebTarget webTarget;") sourceCode.append("\n") - sourceCode.append(" public void method() {\n") + sourceCode.append(" void method() {\n") sourceCode.append(" ${test}\n") sourceCode.append(" }\n") sourceCode.append("}") return InMemoryJavaCompiler.compile(fqnClassName, sourceCode.toString()) } + static boolean tryToCompileJavaWithoutImports(String fqn, String test) { + InMemoryJavaCompiler.compile(fqn, test) + return true + } + + static boolean tryToCompileGroovyWithoutImports(String test) { + new GroovyShell(SyntaxChecker.classLoader).parse(test) + return true + } + } diff --git a/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/LoanApplicationServiceTests.java b/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/LoanApplicationServiceTests.java index 0fae64d3da..5910bd9dcd 100644 --- a/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/LoanApplicationServiceTests.java +++ b/tests/spring-cloud-contract-stub-runner-context-path/src/test/java/com/example/loan/LoanApplicationServiceTests.java @@ -4,12 +4,14 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.contract.stubrunner.StubFinder; import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; import com.example.loan.model.Client; import com.example.loan.model.LoanApplication; @@ -20,19 +22,28 @@ import static org.assertj.core.api.Assertions.assertThat; // tag::autoconfigure_stubrunner[] @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment=WebEnvironment.NONE) +@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT) @AutoConfigureStubRunner(repositoryRoot = "classpath:m2repo/repository/", - ids = { "org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer" }) + ids = { "org.springframework.cloud.contract.verifier.stubs:contextPathFraudDetectionServer" }) @DirtiesContext public class LoanApplicationServiceTests { // end::autoconfigure_stubrunner[] @Autowired private LoanApplicationService service; @Autowired private StubFinder stubFinder; + @LocalServerPort Integer port; @Before public void setPort() { - this.service.setFraudUrl(this.stubFinder.findStubUrl("fraudDetectionServer").toString()); + this.service.setFraudUrl(this.stubFinder.findStubUrl("contextPathFraudDetectionServer").toString() + "/fraud-path/"); + } + + @Test + public void shouldStartThisAppWithContextPath() { + String response = new RestTemplate() + .getForObject("http://localhost:" + this.port + "/my-path/health", String.class); + + assertThat(response).isNotEmpty(); } @Test diff --git a/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/contextPathFraudDetectionServer/0.0.1-SNAPSHOT/contextPathFraudDetectionServer-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/contextPathFraudDetectionServer/0.0.1-SNAPSHOT/contextPathFraudDetectionServer-0.0.1-SNAPSHOT-stubs.jar new file mode 100644 index 0000000000000000000000000000000000000000..5dccb5df1c57b08b2d7b93694687a22a68c57586 GIT binary patch literal 1916 zcmWIWW@h1HVBp|jSmxB}!2kqIAOZ+Df!NnI#8KDN&rP41Apk|;rh2A#(m(~0KrDi+ z(AUw=)6F$FM9(go1*!b6O+x<;PC7%2UUZ9wu9CmAeuSn&qx(mP0xC)jVIc}&1Qx@YvFEV5v^--l$dAn zeD)L>oAOt8>b_5vag&LAKKUtkdqeaiHf8f8GOf99Huu&qF`ayJ`jdcz_TOyNjrRwx zwgM*sNYs=8qvqHX<@d6{08|A=jU72r z-_yH;SXWp_2slQV#?D9y2#T>jGTAG1N`3m9B@znWotlxbgcFk znL{ikb%Hr}yU#eL1kBt&Rdd@FF3%$t_of^81a$ET8C}ck)9{X5!o9x#MrTQ&DD3e1Xr3x4+X+j0YMZg3-3y6(~jg;WTN{`f> zoP0z!*#V4%O_&j}`dLswfR>l8j<2p>V9Ogxm6p0%D$JWy7ZsS4*_G#*YzF= z)d=Go71OUwy7FYwnLm3TTv@c~iqeM6n61Dh$H*kYjJvc0dJ_l)7~VR9Xn5I&t_{8H z11Sc9C5@Uu60QxYBt$m>q#C)H1@R#kmjP*bsfJG*sQ7~bkbCC?nQ(2`3qz1$a?H32 zLkX~1zzoRH(0GrqVfc$iR!Gr^RxBbr05i)YJD?I6HUxZw=>W6>0O$r#0f5I1m?<3D j4SXy_x&bY{1MLN+cMN-3*+3!84un-e-@O5v&cFZw1D1Ok literal 0 HcmV?d00001 diff --git a/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT.pom b/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/contextPathFraudDetectionServer/0.0.1-SNAPSHOT/contextPathFraudDetectionServer-0.0.1-SNAPSHOT.pom similarity index 94% rename from tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT.pom rename to tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/contextPathFraudDetectionServer/0.0.1-SNAPSHOT/contextPathFraudDetectionServer-0.0.1-SNAPSHOT.pom index 40610c8cdb..02a7f412cb 100644 --- a/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT.pom +++ b/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/contextPathFraudDetectionServer/0.0.1-SNAPSHOT/contextPathFraudDetectionServer-0.0.1-SNAPSHOT.pom @@ -19,7 +19,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 org.springframework.cloud.contract.verifier.stubs - fraudDetectionServer + contextPathFraudDetectionServer 0.0.1-SNAPSHOT pom diff --git a/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/maven-metadata.xml b/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/contextPathFraudDetectionServer/maven-metadata.xml similarity index 94% rename from tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/maven-metadata.xml rename to tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/contextPathFraudDetectionServer/maven-metadata.xml index 281b9b3b80..668142b2f3 100644 --- a/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/maven-metadata.xml +++ b/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/contextPathFraudDetectionServer/maven-metadata.xml @@ -17,7 +17,7 @@ org.springframework.cloud.contract.verifier.stubs - fraudDetectionServer + contextPathFraudDetectionServer 0.0.1-SNAPSHOT diff --git a/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-context-path/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT-stubs.jar deleted file mode 100644 index fdb70e383f00ec1a31fd06d81aa618347b8aa5b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1897 zcmWIWW@h1HVBp|jSmxB}!2kqIAOZ+Df!NnI#8KDN&rP41Apk|;rh2A#(m(~0KrDi+ z(AUw=)6F$FM9Q}aq3i`_uh=w%h>=Y@uFGBCR?

Ai(h6<{MwahD zlUYEPO`YKDf7pPh?fqZTiLE9AOJ5&*uI6jNTlgTP_uZB^0#VLZo0gcoT$1^!Uj3xi zi^3-YC+prG|F)Z-qmgTW_7%1hGv4Ip7?-NXu>9@sx>|Y6@ORS(h1?4TQ>#rQmmGhy z`&2onF`M?vx!ZC)*ELFVUsxaI8GW$!`OQxs|7Z$zoqXwb_0oisEj@R3M>M|AJ`~I_ zQzKv&iz%1;3*NTt=G)7^^Do}5`^e1o3HeZ_Sr<~2P( zH|KW#zDb8>96DXIt74fzkatY-vuQJwH#hIS^(%df(w-krM5KNk-@82KnLgJnc~DA1 zM2gvpoP`p=ppybdiXAzT;+J1SY`mQHJ8U4(^1hZUyEQ~W(f4%yQm%mf0)?i6tSW<( ziv-!GI;H&mEnmgsMH^J#SL~lVH|>DLl^@B0(h0%WF3i^2CiaeX@%mY}ZL|%3 zweJikKn{gkJOx;e1dsn^|PRW04*WXbl^_R{e!^wjnA)jN6SJhz{&mbWg`>|>$aKwmL3i7?|X+JN2z0s)4% zjvyLd(4lKXFX%vuL10OvCXj?{Ln`*rO#rDzE>l5#h{a_<8eW9q(*`Q*AOPgv`9LOI z8}^b9WSAT?B2rOHJ_)c{z&yv$(0GrqVff2IR!BLBRtBOw05z8*JD?aCHUxZw=>W7m lPn;W2Q#P_2m|2K)16n%AWiKlmD1_O8unOqAn?TbU7yzYKYMcN7