:Merge branch '1.0.x'

This commit is contained in:
Marcin Grzejszczak
2017-04-25 13:11:32 +02:00
6 changed files with 162 additions and 24 deletions

View File

@@ -131,15 +131,6 @@ org.springframework.cloud.contract.spec.Contract.make {
}
/*
Since we don't want to force on the user to hardcode values of fields that are dynamic
(timestamps, database ids etc.), one can parametrize those entries. If you wrap your field's
value in a `$(...)` or `value(...)` and provide a dynamic value of a field then
the concrete value will be generated for you. If you want to be really explicit about
which side gets which value you can do that by using the `value(consumer(...), producer(...))` notation.
That way what's present in the `consumer` section will end up in the produced stub. What's
there in the `producer` will end up in the autogenerated test. If you provide only the
regular expression side without the concrete value then Spring Cloud Contract will generate one for you.
From the Consumer perspective, when shooting a request in the integration test:
(1) - If the consumer sends a request
@@ -410,15 +401,6 @@ org.springframework.cloud.contract.spec.Contract.make {
}
/*
Since we don't want to force on the user to hardcode values of fields that are dynamic
(timestamps, database ids etc.), one can parametrize those entries. If you wrap your field's
value in a `$(...)` or `value(...)` and provide a dynamic value of a field then
the concrete value will be generated for you. If you want to be really explicit about
which side gets which value you can do that by using the `value(consumer(...), producer(...))` notation.
That way what's present in the `consumer` section will end up in the produced stub. What's
there in the `producer` will end up in the autogenerated test. If you provide only the
regular expression side without the concrete value then Spring Cloud Contract will generate one for you.
From the Consumer perspective, when shooting a request in the integration test:
(1) - If the consumer sends a request
@@ -814,6 +796,12 @@ For `stub-runner` use `spring-cloud-starter-stub-runner` and when you're using a
Below you can find some resources related to Spring Cloud Contract Verifier and Stub Runner. Note that some can be outdated since the Spring Cloud Contract Verifier project
is under constant development.
===== Spring Cloud Contract video
You can check out the video from the Warsaw JUG about Spring Cloud Contract:
video::sAAklvxmPmk[youtube,start=538,width=640,height=480]
===== Readings
- http://www.slideshare.net/MarcinGrzejszczak/stick-to-the-rules-consumer-driven-contracts-201507-confitura[Slides from Marcin Grzejszczak's talk about Accurest]
@@ -1501,6 +1489,49 @@ public class WiremockForDocsClassRuleTests {
The use `@ClassRule` means that the server will shut down after all the methods in this class.
== Relaxed SSL Validation for Rest Template
WireMock allows you to stub a "secure" server with an "https" URL protocol. If your application wants to
contact that stub server in an integration test, then it will find that the SSL certificates are not
valid (it's the usual problem with self-installed certificates). The best option is often to just
re-configure the client to use "http", but if that's not open to you then you can ask Spring to configure
an HTTP client that ignores SSL validation errors (just for tests).
To make this work with minimum fuss you need to be using the Spring Boot `RestTemplateBuilder` in your app,
e.g.
[source,java,indent=0]
----
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
----
This is because the builder is passed through callbacks to initalize it, so the SSL validation can be set up
in the client at that point. This will happen automatically in your test if you are using the
`@AutoConfigureWireMock` annotation (or the stub runner). If you are using the JUnit `@Rule` approach you need
to add the `@AutoConfigureHttpClient` annotation as well:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:6443")
@AutoConfigureHttpClient
public class WiremockHttpsServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().httpsPort(6443));
...
}
----
If you are using `spring-boot-starter-test` then you will have the Apache HTTP client on the classpath and it will
be selected by the `RestTemplateBuilder` and configured to ignore SSL errors. If you are using the default `java.net`
client you don't need the annotation (but it won't do any harm). There is no support currently for other clients, but
it may be added in future releases.
== WireMock and Spring MVC Mocks
Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a

View File

@@ -73,9 +73,6 @@ class TestGenerator {
}
int generate() {
if (!configProperties.basePackageForTests) {
}
generateTestClasses(configProperties.basePackageForTests)
return counter.get()
}

View File

@@ -33,6 +33,8 @@ import org.springframework.cloud.contract.verifier.config.ContractVerifierConfig
@PackageScope
class ClassBuilder {
private static final String SEPARATOR = "_REPLACEME_"
private final String className
private final String packageName
private final String baseClass
@@ -70,7 +72,7 @@ class ClassBuilder {
}
protected static String retrieveBaseClass(ContractVerifierConfigProperties properties, String includedDirectoryRelativePath) {
String contractPackage = includedDirectoryRelativePath.replace(File.separator, '.')
String contractPackage = includedDirectoryRelativePath.replace(File.separator, SEPARATOR)
// package mapping takes super precedence
if (properties.baseClassMappings) {
Map.Entry<String, String> mapping = properties.baseClassMappings.find { String pattern, String fqn ->
@@ -88,7 +90,7 @@ class ClassBuilder {
}
private static String generateDefaultBaseClassName(String classPackage, ContractVerifierConfigProperties properties) {
String[] splitPackage = NamesUtil.convertIllegalPackageChars(classPackage).split("\\.")
String[] splitPackage = NamesUtil.convertIllegalPackageChars(classPackage).split(SEPARATOR)
if (splitPackage.size() > 1) {
String last = NamesUtil.capitalize(splitPackage[-1])
String butLast = NamesUtil.capitalize(splitPackage[-2])

View File

@@ -106,6 +106,6 @@ class NamesUtil {
* Converts illegal package characters to underscores
*/
static String convertIllegalPackageChars(String packageName) {
return packageName.replace('-', '_').replace(" ", "_")
return packageName.replaceAll('[_\\- .]', '_')
}
}

View File

@@ -60,4 +60,12 @@ class ClassBuilderSpec extends Specification {
ClassBuilder.retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SuperpackageBase'
}
def "should return a class from the generated path by when external contracts are picked"() {
given:
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(packageWithBaseClasses: "foo.Bar")
String contractRelativeFolder = ["org","springframework","cloud","contract","verifier","tests","META_INF","com.example","hello_world","1.0.0"].join(File.separator)
expect:
ClassBuilder.retrieveBaseClass(props, contractRelativeFolder) == 'foo.Bar.Hello_world1_0_0Base'
}
}

View File

@@ -0,0 +1,100 @@
package org.springframework.cloud.contract.verifier.util
import spock.lang.Specification
/**
* @author Marcin Grzejszczak
*/
class NamesUtilTestSpec extends Specification {
def "should return the whole string before the last one"() {
given:
String string = "a.b.c.d.e"
expect:
"a.b.c.d" == NamesUtil.beforeLast(string, ".")
}
def "should return empty string when no token was found for before last"() {
given:
String string = "a.b.c.d.e"
expect:
"" == NamesUtil.beforeLast(string, "/")
}
def "should return first token after the last one"() {
given:
String string = "a.b.c.d.e"
expect:
"e" == NamesUtil.afterLast(string, ".")
}
def "should return the input string when no token was found for after last"() {
given:
String string = "a.b.c.d.e"
expect:
string == NamesUtil.afterLast(string, "/")
}
def "should return first token after the last dot"() {
given:
String string = "a.b.c.d.e"
expect:
"e" == NamesUtil.afterLastDot(string)
}
def "should return the input string when no token was found for after last dot"() {
given:
String string = "abcde"
expect:
string == NamesUtil.afterLastDot(string)
}
def "should return camel case version of a string"() {
given:
String string = "BlaBlaBla"
expect:
"blaBlaBla" == NamesUtil.camelCase(string)
}
def "should return capitalized version of a string"() {
given:
String string = "blaBlaBla"
expect:
"BlaBlaBla" == NamesUtil.capitalize(string)
}
def "should return all text to last dot"() {
given:
String string = "a.b.c.d.e"
expect:
"a.b.c.d" == NamesUtil.toLastDot(string)
}
def "should return the input string when no token was found for to last dot"() {
given:
String string = "abcde"
expect:
string == NamesUtil.toLastDot(string)
}
def "should convert a package notation to directory"() {
given:
String string = "a.b.c.d.e"
expect:
"a/b/c/d/e".replace("/", File.separator) == NamesUtil.packageToDirectory(string)
}
def "should convert a directory notation to package"() {
given:
String string = "a/b/c/d/e".replace("/", File.separator)
expect:
"a.b.c.d.e" == NamesUtil.directoryToPackage(string)
}
def "should convert all illegal package chars to legal ones"() {
given:
String string = "a-b c.1.0.x"
expect:
"a_b_c_1_0_x" == NamesUtil.convertIllegalPackageChars(string)
}
}