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 0000000000..5dccb5df1c Binary files /dev/null and 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 differ 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 fdb70e383f..0000000000 Binary files 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 and /dev/null differ