From a2ff9f6e97c2864acbd4754bdd09cc915539b0e7 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Fri, 23 Sep 2016 14:04:14 +0000 Subject: [PATCH] Sync docs from master to gh-pages --- spring-cloud-contract.html | 439 ++++++++++++++----------------------- 1 file changed, 163 insertions(+), 276 deletions(-) diff --git a/spring-cloud-contract.html b/spring-cloud-contract.html index c1f365053d..870b348df8 100644 --- a/spring-cloud-contract.html +++ b/spring-cloud-contract.html @@ -440,7 +440,6 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b @@ -474,9 +473,9 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
  • Producer
  • +
  • Can I have multiple base classes for tests?
  • -
  • Can I have multiple base classes for tests?
  • Spring Cloud Contract Verifier HTTP @@ -1130,149 +1129,42 @@ public void shouldBeRejectedDueToAbnormalLoanAmount() {
    -
    package contracts
    +
    Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/src/test/resources/contracts/shouldMarkClientAsFraud.groovy[]
     
    -org.springframework.cloud.contract.spec.Contract.make {
    -			request { // (1)
    -				method 'PUT' // (2)
    -				url '/fraudcheck' // (3)
    -				body([ // (4)
    -					clientId: value(consumer(regex('[0-9]{10}'))),
    -					loanAmount: 99999
    -					])
    -				headers { // (5)
    -					header('Content-Type', 'application/vnd.fraud.v1+json')
    -				}
    -			}
    -			response { // (6)
    -				status 200 // (7)
    -				body([ // (8)
    -					fraudCheckStatus: "FRAUD",
    -					rejectionReason: "Amount too high"
    -				])
    -				headers { // (9)
    -					 header('Content-Type': value(
    -							 producer(regex('application/vnd.fraud.v1.json.*')),
    -							 consumer('application/vnd.fraud.v1+json'))
    -					 )
    -				}
    -			}
    -}
    -
    -/*
    -Since we don't want to force on the user to hardcode values of fields that are dynamic
    -(timestamps, database ids etc.), one can provide parametrize those entries by using the
    -`value(consumer(...), producer(...))` method. 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
    -(2) - With the "PUT" method
    -(3) - to the URL "/fraudcheck"
    -(4) - with the JSON body that
    - * has a field `clientId` that matches a regular expression `[0-9]{10}`
    - * has a field `loanAmount` that is equal to `99999`
    -(5) - with header `Content-Type` equal to `application/vnd.fraud.v1+json`
    -(6) - then the response will be sent with
    -(7) - status equal `200`
    -(8) - and JSON body equal to
    - { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    -(9) - with header `Content-Type` equal to `application/vnd.fraud.v1+json`
    -
    -From the Producer perspective, in the autogenerated producer-side test:
    -
    -(1) - A request will be sent to the producer
    -(2) - With the "PUT" method
    -(3) - to the URL "/fraudcheck"
    -(4) - with the JSON body that
    - * has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
    - * has a field `loanAmount` that is equal to `99999`
    -(5) - with header `Content-Type` equal to `application/vnd.fraud.v1+json`
    -(6) - then the test will assert if the response has been sent with
    -(7) - status equal `200`
    -(8) - and JSON body equal to
    - { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    -(9) - with header `Content-Type` matching `application/vnd.fraud.v1+json.*`
    - */
    -
    -
    -
    -

    The Contract is written using a statically typed Groovy DSL. You might be wondering what are those -value(client(…​), server(…​)) parts. By using this notation Spring Cloud Contract allows you to +The Contract is written using a statically typed Groovy DSL. You might be wondering what are those +`value(client(...), server(...))` parts. By using this notation Spring Cloud Contract allows you to define parts of a JSON / URL / etc. which are dynamic. In case of an identifier or a timestamp you -don’t want to hardcode a value. You want to allow some different ranges of values. That’s why for +don't want to hardcode a value. You want to allow some different ranges of values. That's why for the consumer side you can set regular expressions matching those values. You can provide the body either by means of a map notation or String with interpolations. -Consult the docs -for more information. We highly recommend using the map notation!

    +https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_contract_dsl[Consult the docs +for more information.] We highly recommend using the map notation! + +TIP: It's really important that you understand the map notation to set up contracts. Please read the +http://groovy-lang.org/json.html[Groovy docs regarding JSON] + +The aforementioned contract is an agreement between two sides that: + +- if an HTTP request is sent with +** a method `PUT` on an endpoint `/fraudcheck` +** JSON body with `clientId` matching the regular expression `[0-9]{10}` and `loanAmount` equal to `99999` +** and with a header `Content-Type` equal to `application/vnd.fraud.v1+json` +- then an HTTP response would be sent to the consumer that +** has status `200` +** contains JSON body with the `fraudCheckStatus` field containing a value `FRAUD` and the `rejectionReason` field having value `Amount too high` +** and a `Content-Type` header with a value of `application/vnd.fraud.v1+json` + +Once we're ready to check the API in practice in the integration tests we need to just install the stubs locally + +*add the Spring Cloud Contract Verifier plugin* + +We can add either Maven or Gradle plugin - in this example we'll show how to add Maven. First we need to add the `Spring Cloud Contract` BOM. + +[source,xml,indent=0]
    -
    - - - - - -
    -
    Tip
    -
    -It’s really important that you understand the map notation to set up contracts. Please read the -Groovy docs regarding JSON -
    -

    The aforementioned contract is an agreement between two sides that:

    -
    -
    -
      -
    • -

      if an HTTP request is sent with

      -
      -
        -
      • -

        a method PUT on an endpoint /fraudcheck

        -
      • -
      • -

        JSON body with clientId matching the regular expression [0-9]{10} and loanAmount equal to 99999

        -
      • -
      • -

        and with a header Content-Type equal to application/vnd.fraud.v1+json

        -
      • -
      -
      -
    • -
    • -

      then an HTTP response would be sent to the consumer that

      -
      -
        -
      • -

        has status 200

        -
      • -
      • -

        contains JSON body with the fraudCheckStatus field containing a value FRAUD and the rejectionReason field having value Amount too high

        -
      • -
      • -

        and a Content-Type header with a value of application/vnd.fraud.v1+json

        -
      • -
      -
      -
    • -
    -
    -
    -

    Once we’re ready to check the API in practice in the integration tests we need to just install the stubs locally

    -
    -
    -

    add the Spring Cloud Contract Verifier plugin

    -
    -
    -

    We can add either Maven or Gradle plugin - in this example we’ll show how to add Maven. First we need to add the Spring Cloud Contract BOM.

    -
    -
    -
    -
    <dependencyManagement>
    +

    <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> @@ -1282,15 +1174,17 @@ It’s really important that you understand the map notation to set up contr <scope>import</scope> </dependency> </dependencies> -</dependencyManagement>

    -
    -
    -
    -

    Next, the Spring Cloud Contract Verifier Maven plugin

    +</dependencyManagement>

    -
    <plugin>
    +
    Next, the `Spring Cloud Contract Verifier` Maven plugin
    +
    +[source,xml,indent=0]
    +
    +
    +
    +

    <plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <version>${spring-cloud-contract.version}</version> @@ -1298,73 +1192,73 @@ It’s really important that you understand the map notation to set up contr <configuration> <packageWithBaseClasses>com.example.fraud</packageWithBaseClasses> </configuration> -</plugin> -

    - -
    -

    Since the plugin was added we get the Spring Cloud Contract Verifier features which from the provided contracts:

    -
    -
    -
      -
    • -

      generate and run tests

      -
    • -
    • -

      produce and install stubs

      -
    • -
    -
    -
    -

    We don’t want to generate tests since we, as consumers, want only to play with the stubs. That’s why we need to skip the tests generation and execution. When we execute:

    +</plugin>

    -
    cd local-http-server-repo
    -./mvnw clean install -DskipTests
    +
    Since the plugin was added we get the `Spring Cloud Contract Verifier` features which from the provided contracts:
    +
    +- generate and run tests
    +- produce and install stubs
    +
    +We don't want to generate tests since we, as consumers, want only to play with the stubs. That's why we need to skip the tests generation and execution. When we execute:
    +
    +[source,bash,indent=0]
    -

    In the logs we’ll see something like this:

    +

    cd local-http-server-repo +./mvnw clean install -DskipTests

    -
    [INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
    -[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar
    -[INFO]
    -[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server ---
    -[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar
    -[INFO]
    -[INFO] --- spring-boot-maven-plugin:1.4.0.BUILD-SNAPSHOT:repackage (default) @ http-server ---
    -[INFO]
    -[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ http-server ---
    +
    In the logs we'll see something like this:
    +
    +[source,bash,indent=0]
    +
    +
    +
    +

    [INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server --- +[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar

    +
    +
    +

    [INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server --- +[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar

    +
    +
    +

    [INFO] --- spring-boot-maven-plugin:1.4.0.BUILD-SNAPSHOT:repackage (default) @ http-server ---

    +
    +
    +

    [INFO] --- maven-install-plugin:2.5.2:install (default-install) @ http-server --- [INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.jar [INFO] Installing /some/path/http-server/pom.xml to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.pom -[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar -

    - -
    -

    This line is extremely important

    +[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar

    -
    [INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar
    +
    This line is extremely important
    +
    +[source,bash,indent=0]
    -

    It’s confirming that the stubs of the http-server have been installed in the local repository.

    -
    -
    -

    run the integration tests

    -
    -
    -

    In order to profit from the Spring Cloud Contract Stub Runner functionality of automatic stub downloading you have to do the following in our consumer side project (Loan Application service).

    -
    -
    -

    Add the Spring Cloud Contract BOM

    +

    [INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar

    -
    <dependencyManagement>
    +
    It's confirming that the stubs of the `http-server` have been installed in the local repository.
    +
    +*run the integration tests*
    +
    +In order to profit from the Spring Cloud Contract Stub Runner functionality of automatic stub downloading you have to do the following in our consumer side project (`Loan Application service`).
    +
    +Add the `Spring Cloud Contract` BOM
    +
    +[source,xml,indent=0]
    +
    +
    +
    +

    <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> @@ -1374,109 +1268,118 @@ It’s really important that you understand the map notation to set up contr <scope>import</scope> </dependency> </dependencies> -</dependencyManagement> -

    - -
    -

    Add the dependency to Spring Cloud Contract Stub Runner

    +</dependencyManagement>

    -
    <dependency>
    +
    Add the dependency to `Spring Cloud Contract Stub Runner`
    +
    +[source,xml,indent=0]
    +
    +
    +
    +

    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-contract-stub-runner</artifactId> <scope>test</scope> -</dependency> -

    - -
    -

    Annotate your test class with @AutoConfigureStubRunner. In the annotation provide the group id and artifact id for the Stub Runner to download stubs of your collaborators. Also provide the offline work switch since you’re playing with the collaborators offline (optional step).

    +</dependency>

    -
    @RunWith(SpringRunner.class)
    +
    Annotate your test class with `@AutoConfigureStubRunner`. In the annotation provide the group id and artifact id for the Stub Runner to download stubs of your collaborators. Also provide the offline work switch since you're playing with the collaborators offline (optional step).
    +
    +[source,groovy,indent=0]
    +
    +
    +
    +

    @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"}, workOffline = true) -public class LoanApplicationServiceTests { -

    - -
    -

    Now if you run your tests you’ll see sth like this:

    +public class LoanApplicationServiceTests {

    -
    2016-07-19 14:22:25.403  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Desired version is + - will try to resolve the latest version
    +
    Now if you run your tests you'll see sth like this:
    +
    +[source,bash,indent=0]
    +
    +
    +
    +

    2016-07-19 14:22:25.403 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Desired version is + - will try to resolve the latest version 2016-07-19 14:22:25.438 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved version is 0.0.1-SNAPSHOT 2016-07-19 14:22:25.439 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolving artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT using remote repositories [] 2016-07-19 14:22:25.451 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar 2016-07-19 14:22:25.465 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar] 2016-07-19 14:22:25.475 INFO 41050 --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacked file to [/var/folders/0p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265] -2016-07-19 14:22:27.737 INFO 41050 --- [ main] o.s.c.c.stubrunner.StubRunnerExecutor : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:0.0.1-SNAPSHOT:stubs=8080}] -

    - -
    -

    Which means that Stub Runner has found your stubs and started a server for app with group id com.example, artifact id http-server with version 0.0.1-SNAPSHOT of the stubs and with stubs classifier on port 8080.

    -
    -
    -

    file a PR

    -
    -
    -

    What we did until now is an iterative process. We can play around with the contract, install it locally and work on the consumer side until we’re happy with the contract.

    -
    -
    -

    Once we’re satisfied with the results and the test passes publish a PR to the server side. Currently the consumer side work is done.

    -
    - -
    -
    Producer side (Fraud Detection server)
    -
    -

    As a developer of the Fraud Detection server (a server to the Loan Issuance service):

    -
    -
    -

    initial implementation

    -
    -
    -

    As a reminder here you can see the initial implementation

    +2016-07-19 14:22:27.737 INFO 41050 --- [ main] o.s.c.c.stubrunner.StubRunnerExecutor : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:0.0.1-SNAPSHOT:stubs=8080}]

    -
    @RequestMapping(
    +
    Which means that Stub Runner has found your stubs and started a server for app with group id `com.example`, artifact id `http-server` with version `0.0.1-SNAPSHOT` of the stubs and with `stubs` classifier on port `8080`.
    +
    +*file a PR*
    +
    +What we did until now is an iterative process. We can play around with the contract, install it locally and work on the consumer side until we're happy with the contract.
    +
    +Once we're satisfied with the results and the test passes publish a PR to the server side. Currently the consumer side work is done.
    +
    +===== Producer side (Fraud Detection server)
    +
    +As a developer of the Fraud Detection server (a server to the Loan Issuance service):
    +
    +*initial implementation*
    +
    +As a reminder here you can see the initial implementation
    +
    +[source,java,indent=0]
    +
    +
    +
    +

    @RequestMapping( value = "/fraudcheck", method = PUT, consumes = FRAUD_SERVICE_JSON_VERSION_1, produces = FRAUD_SERVICE_JSON_VERSION_1) public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) { return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON); -} -

    -
    -
    -

    take over the PR

    +}

    -
    git checkout -b contract-change-pr master
    -git pull https://your-git-server.com/server-side-fork.git contract-change-pr
    +
    *take over the PR*
    +
    +[source,bash,indent=0]
    -

    You have to add the dependencies needed by the autogenerated tests

    +

    git checkout -b contract-change-pr master +git pull https://your-git-server.com/server-side-fork.git contract-change-pr

    -
    	<dependency>
    +
    You have to add the dependencies needed by the autogenerated tests
    +
    +[source,xml,indent=0]
    +
    +
    +
    +
    +
    	<dependency>
     	<groupId>org.springframework.cloud</groupId>
     	<artifactId>spring-cloud-starter-contract-verifier</artifactId>
     	<scope>test</scope>
    -</dependency>
    +</dependency>
    -
    -

    In the configuration of the Maven plugin we passed the baseClassForTests property

    -
    -
    <plugin>
    +
    In the configuration of the Maven plugin we passed the `baseClassForTests` property
    +
    +[source,xml,indent=0]
    +
    +
    +
    +

    <plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <version>${spring-cloud-contract.version}</version> @@ -1484,35 +1387,19 @@ git pull https://your-git-server.com/server-side-fork.git contract-change-pr -

    - -
    -

    That’s because all the generated tests will extend that class. Over there you can set up your Spring Context or whatever is necessary. In our case we’re using Rest Assured MVC to start the server side FraudDetectionController.

    +</plugin>

    -
    package com.example.fraud;
    +
    That's because all the generated tests will extend that class. Over there you can set up your Spring Context or whatever is necessary. In our case we're using http://rest-assured.io/[Rest Assured MVC] to start the server side `FraudDetectionController`.
     
    -import com.example.fraud.FraudDetectionController;
    -import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;
    -
    -import org.junit.Before;
    -
    -public class FraudBase {
    -
    -	@Before
    -	public void setup() {
    -		RestAssuredMockMvc.standaloneSetup(new FraudDetectionController());
    -	}
    -
    -	public void assertThatRejectionReasonIsNull(Object rejectionReason) {
    -		assert rejectionReason == null;
    -	}
    -}
    +[source,java,indent=0]
    +

    Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/src/test/java/com/example/fraud/MvcTest.java[]

    +
    +

    Now, if you run the ./mvnw clean install you would get sth like this:

    @@ -2326,15 +2213,15 @@ when some incompatible changes are done.

    - -
    -

    Can I have multiple base classes for tests?

    +
    +
    Can I have multiple base classes for tests?
    -

    Yes! Check out the Different base classes for contracts sections +

    Yes! Check out the Different base classes for contracts sections of either Gradle or Maven plugins.

    +

    Spring Cloud Contract Verifier HTTP