Split files
This commit is contained in:
588
docs/modules/ROOT/pages/getting-started/cdc.adoc
Normal file
588
docs/modules/ROOT/pages/getting-started/cdc.adoc
Normal file
@@ -0,0 +1,588 @@
|
||||
[[getting-started-cdc]]
|
||||
= Step-by-step Guide to Consumer Driven Contracts (CDC) with Contracts on the Producer Side
|
||||
|
||||
Consider an example of fraud detection and the loan issuance process. The business
|
||||
scenario is such that we want to issue loans to people but do not want them to steal from
|
||||
us. The current implementation of our system grants loans to everybody.
|
||||
|
||||
Assume that `Loan Issuance` is a client to the `Fraud Detection` server. In the current
|
||||
sprint, we must develop a new feature: if a client wants to borrow too much money,
|
||||
we mark the client as a fraud.
|
||||
|
||||
Technical remarks
|
||||
|
||||
* Fraud Detection has an `artifact-id` of `http-server`.
|
||||
* Loan Issuance has an `artifact-id` of `http-client`.
|
||||
* Both have a `group-id` of `com.example`.
|
||||
* For the sake of this example, the `Stub Storage` is Nexus/Artifactory.
|
||||
|
||||
Social remarks
|
||||
|
||||
* Both the client and the server development teams need to communicate directly and
|
||||
discuss changes while going through the process.
|
||||
* CDC is all about communication.
|
||||
|
||||
The server-side code is available under Spring Cloud Contract's repository `samples/standalone/dsl/http-server` path, and the client-side code is available under Spring Cloud Contract's repository `samples/standalone/dsl/http-client` path.
|
||||
|
||||
TIP: In this case, the producer owns the contracts. Physically, all the contracts are
|
||||
in the producer's repository.
|
||||
|
||||
[[getting-started-cdc-technical-note]]
|
||||
== Technical Note
|
||||
|
||||
If you use the SNAPSHOT, Milestone, or Release Candidate versions, you need to add the
|
||||
following section to your build:
|
||||
|
||||
====
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/pom.xml[tags=repos,indent=0]
|
||||
----
|
||||
////
|
||||
|
||||
////
|
||||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||||
.Gradle
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/build.gradle[tags=deps_repos,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
For simplicity, we use the following acronyms:
|
||||
|
||||
- Loan Issuance (LI): The HTTP client
|
||||
- Fraud Detection (FD): The HTTP server
|
||||
- SCC: Spring Cloud Contract
|
||||
|
||||
[[getting-started-cdc-consumer]]
|
||||
== The Consumer Side (Loan Issuance)
|
||||
|
||||
As a developer of the Loan Issuance service (a consumer of the Fraud Detection server), you might do the following steps:
|
||||
|
||||
. Start doing TDD by writing a test for your feature.
|
||||
. Write the missing implementation.
|
||||
. Clone the Fraud Detection service repository locally.
|
||||
. Define the contract locally in the repository of the fraud detection service.
|
||||
. Add the Spring Cloud Contract (SCC) plugin.
|
||||
. Run the integration tests.
|
||||
. File a pull request.
|
||||
. Create an initial implementation.
|
||||
. Take over the pull request.
|
||||
. Write the missing implementation.
|
||||
. Deploy your application.
|
||||
. Work online.
|
||||
|
||||
We start with the loan issuance flow, which the following UML diagram shows:
|
||||
|
||||
[plantuml, getting-started-cdc-client, png]
|
||||
----
|
||||
"Loan\nIssuance"->"Loan\nIssuance": start doing TDD\nby writing a test\nfor your feature
|
||||
"Loan\nIssuance"->"Loan\nIssuance": write the \nmissing implementation
|
||||
"Loan\nIssuance"->"Loan\nIssuance": run a test - it fails\ndue to no server running
|
||||
"Loan\nIssuance"->"Fraud\nDetection\nClone": clone the repository
|
||||
"Fraud\nDetection\nClone"->"Fraud\nDetection\nClone": add missing dependencies\n& define contracts
|
||||
"Fraud\nDetection\nClone"->"Fraud\nDetection\nClone": add the SCC plugin
|
||||
"Fraud\nDetection\nClone"->"FD \nClone Build": install the stubs locally
|
||||
"FD \nClone Build"->"SCC Plugin \nin FD Clone": generate stubs \nand stubs \nartifact (e.g. stubs-jar)
|
||||
"SCC Plugin \nin FD Clone"->"FD \nClone Build": stubs and artifacts\ngenerated
|
||||
"FD \nClone Build"->"Local storage": install the stubs locally
|
||||
"Local storage"->"FD \nClone Build": stub sucessfully installed
|
||||
"FD \nClone Build"->"Fraud\nDetection\nClone": build successful
|
||||
"Loan\nIssuance"->"Loan\nIssuance": add a SCC\nStub Runner\ndependency\nand setup
|
||||
"Loan\nIssuance"->"LI\nSCC\nStub Runner": start stubs\nof FD from\nlocal storage
|
||||
"LI\nSCC\nStub Runner"->"Local storage": find stubs of [FD]
|
||||
"Local storage"->"LI\nSCC\nStub Runner": stubs of [FD] found
|
||||
"LI\nSCC\nStub Runner"->"FD stub": run stubs of [FD]
|
||||
"FD stub"->"LI\nSCC\nStub Runner": [FD] stub is running
|
||||
"LI\nSCC\nStub Runner"->"Loan\nIssuance": stubs running and ready for the test
|
||||
"Loan\nIssuance"->"Loan\nIssuance": run a test
|
||||
"Loan\nIssuance"->"FD stub": the test\nsends a request\nto the running stub
|
||||
"FD stub"->"Loan\nIssuance": stub responds successfuly
|
||||
"Loan\nIssuance"->"Loan\nIssuance": the test passes successfully
|
||||
"Loan\nIssuance"->"Fraud\nDetection": send a pull request\nwith the\nsuggested contracts
|
||||
----
|
||||
|
||||
[[getting-started-cdc-consumer-start]]
|
||||
=== Start Doing TDD by Writing a Test for Your Feature
|
||||
|
||||
The following listing shows a test that we might use to check whether a loan amount is too
|
||||
large:
|
||||
|
||||
====
|
||||
[source,groovy,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java[tags=client_tdd,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
Assume that you have written a test of your new feature. If a loan application for a big
|
||||
amount is received, the system should reject that loan application with some description.
|
||||
|
||||
[[getting-started-cdc-consumer-write]]
|
||||
=== Write the Missing Implementation
|
||||
|
||||
At some point in time, you need to send a request to the Fraud Detection service. Assume
|
||||
that you need to send the request containing the ID of the client and the amount the
|
||||
client wants to borrow. You want to send it to the `/fraudcheck` URL by using the `PUT` method.
|
||||
To do so, you might use code similar to the following:
|
||||
|
||||
====
|
||||
[source,groovy,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-client/src/main/java/com/example/loan/LoanApplicationService.java[tags=client_call_server,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
For simplicity, the port of the Fraud Detection service is set to `8080`, and the
|
||||
application runs on `8090`.
|
||||
|
||||
NOTE: If you start the test at this point, it breaks, because no service currently runs on port
|
||||
`8080`.
|
||||
|
||||
[[getting-started-cdc-consumer-clone]]
|
||||
=== Clone the Fraud Detection service repository locally
|
||||
|
||||
You can start by playing around with the server side contract. To do so, you must first
|
||||
clone it, by running the following command:
|
||||
|
||||
====
|
||||
[source,bash,indent=0]
|
||||
----
|
||||
$ git clone https://your-git-server.com/server-side.git local-http-server-repo
|
||||
----
|
||||
====
|
||||
|
||||
[[getting-started-cdc-consumer-define]]
|
||||
=== Define the Contract Locally in the Repository of the Fraud Detection Service
|
||||
|
||||
As a consumer, you need to define what exactly you want to achieve. You need to formulate
|
||||
your expectations. To do so, write the following contract:
|
||||
|
||||
IMPORTANT: Place the contract in the `src/test/resources/contracts/fraud` folder. The `fraud` folder
|
||||
is important because the producer's test base class name references that folder.
|
||||
|
||||
The following example shows our contract, in both Groovy and YAML:
|
||||
|
||||
====
|
||||
[source,groovy,indent=0,role="primary"]
|
||||
.groovy
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy[]
|
||||
----
|
||||
////
|
||||
|
||||
////
|
||||
[source,yaml,indent=0,role="secondary"]
|
||||
.yaml
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/src/test/resources/contracts/yml/fraud/shouldMarkClientAsFraud.yml[]
|
||||
----
|
||||
====
|
||||
|
||||
The YML contract is quite straightforward. However, when you take a look at the contract
|
||||
written with a statically typed Groovy DSL, you might wonder what the
|
||||
`value(client(...), server(...))` parts are. By using this notation, Spring Cloud
|
||||
Contract lets you define parts of a JSON block, a URL, or other structure that is dynamic. In the case
|
||||
of an identifier or a timestamp, you need not hardcode a value. You want to allow some
|
||||
different ranges of values. To enable ranges of values, you can set regular expressions
|
||||
that match those values for the consumer side. You can provide the body by means of either
|
||||
a map notation or a String with interpolations. We highly recommend using the map notation.
|
||||
|
||||
TIP: To set up contracts, you must understand the map notation. See the
|
||||
https://groovy-lang.org/json.html[Groovy docs regarding JSON].
|
||||
|
||||
The previously shown contract is an agreement between two sides that:
|
||||
|
||||
* If an HTTP request is sent with all of:
|
||||
** A `PUT` method on the `/fraudcheck` endpoint
|
||||
** A JSON body with a `client.id` that matches the regular expression `[0-9]{10}` and
|
||||
`loanAmount` equal to `99999`
|
||||
** A `Content-Type` header with a value of `application/vnd.fraud.v1+json`
|
||||
* Then an HTTP response is sent to the consumer that
|
||||
** Has status `200`
|
||||
** Contains a JSON body with the `fraudCheckStatus` field containing a value of `FRAUD` and
|
||||
the `rejectionReason` field having a value of `Amount too high`
|
||||
** Has a `Content-Type` header with a value of `application/vnd.fraud.v1+json`
|
||||
|
||||
Once you are ready to check the API in practice in the integration tests, you need to
|
||||
install the stubs locally.
|
||||
|
||||
[[getting-started-cdc-consumer-add]]
|
||||
=== Add the Spring Cloud Contract Verifier Plugin
|
||||
|
||||
We can add either a Maven or a Gradle plugin. In this example, we show how to add Maven.
|
||||
First, we add the `Spring Cloud Contract` BOM, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/pom.xml[tags=contract_bom,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
Next, add the `Spring Cloud Contract Verifier` Maven plugin, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/pom.xml[tags=contract_maven_plugin,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
Since the plugin was added, you get the `Spring Cloud Contract Verifier` features, which,
|
||||
from the provided contracts:
|
||||
|
||||
- Generate and run tests
|
||||
- Produce and install stubs
|
||||
|
||||
You do not want to generate tests, since you, as the consumer, want only to play with the
|
||||
stubs. You need to skip the test generation and invokation. To do so, run the following commands:
|
||||
|
||||
====
|
||||
[source,bash,indent=0]
|
||||
----
|
||||
$ cd local-http-server-repo
|
||||
$ ./mvnw clean install -DskipTests
|
||||
----
|
||||
====
|
||||
|
||||
Once you run those commands, you should you see something like the following content in the logs:
|
||||
|
||||
====
|
||||
[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]
|
||||
[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.5.5.BUILD-SNAPSHOT:repackage (default) @ http-server ---
|
||||
[INFO]
|
||||
[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
|
||||
----
|
||||
====
|
||||
|
||||
The following line is extremely important:
|
||||
|
||||
====
|
||||
[source,bash,indent=0]
|
||||
----
|
||||
[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
|
||||
----
|
||||
====
|
||||
|
||||
It confirms that the stubs of the `http-server` have been installed in the local
|
||||
repository.
|
||||
|
||||
[[getting-started-cdc-consumer-run]]
|
||||
=== Running the Integration Tests
|
||||
|
||||
In order to profit from the Spring Cloud Contract Stub Runner functionality of automatic
|
||||
stub downloading, you must do the following in your consumer side project (`Loan
|
||||
Application service`):
|
||||
|
||||
. Add the `Spring Cloud Contract` BOM, as follows:
|
||||
+
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-client/pom.xml[tags=contract_bom,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
. Add the dependency to `Spring Cloud Contract Stub Runner`, as follows:
|
||||
+
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-client/pom.xml[tags=stub_runner,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
. Annotate your test class with `@AutoConfigureStubRunner`. In the annotation, provide the
|
||||
`group-id` and `artifact-id` for the Stub Runner to download the stubs of your
|
||||
collaborators.
|
||||
+
|
||||
====
|
||||
[source,groovy,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java[tags=autoconfigure_stubrunner,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
. (Optional) Because you are playing with the collaborators offline, you
|
||||
can also provide the offline work switch (`StubRunnerProperties.StubsMode.LOCAL`).
|
||||
|
||||
Now, when you run your tests, you see something like the following output in the logs:
|
||||
|
||||
====
|
||||
[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}]
|
||||
----
|
||||
====
|
||||
|
||||
This output means that Stub Runner has found your stubs and started a server for your application
|
||||
with a group ID of `com.example` and an artifact ID of `http-server` with version `0.0.1-SNAPSHOT` of
|
||||
the stubs and with the `stubs` classifier on port `8080`.
|
||||
|
||||
[[getting-started-cdc-consumer-file]]
|
||||
=== Filing a Pull Request
|
||||
|
||||
What you have done until now is an iterative process. You can play around with the
|
||||
contract, install it locally, and work on the consumer side until the contract works as
|
||||
you wish.
|
||||
|
||||
Once you are satisfied with the results and the test passes, you can publish a pull request to
|
||||
the server side. Currently, the consumer side work is done.
|
||||
|
||||
[[getting-started-cdc-producer]]
|
||||
== The Producer Side (Fraud Detection server)
|
||||
|
||||
As a developer of the Fraud Detection server (a server to the Loan Issuance service), you
|
||||
might want to:
|
||||
|
||||
- Take over the pull request
|
||||
- Write the missing implementation
|
||||
- Deploy the application
|
||||
|
||||
The following UML diagram shows the fraud detection flow:
|
||||
|
||||
[plantuml, getting-started-cdc-server, png]
|
||||
----
|
||||
"Fraud\nDetection"->"Fraud\nDetection": take over the\n pull request
|
||||
"Fraud\nDetection"->"Fraud\nDetection": setup\nSpring Cloud\nContract plugin
|
||||
"Fraud\nDetection"->"Fraud\nDetection\nBuild": run the build
|
||||
"Fraud\nDetection\nBuild"->"SCC Plugin": generate tests\nstubs \nand stubs artifact \n(e.g. stubs-jar)
|
||||
"SCC Plugin"->"Fraud\nDetection\nBuild": tests and stubs generated
|
||||
"Fraud\nDetection\nBuild"->"Fraud\nDetection\nBuild": run tests
|
||||
"Fraud\nDetection\nBuild"->"Fraud\nDetection": generated tests failed!
|
||||
"Fraud\nDetection"->"Fraud\nDetection": setup\nbase classes\nfor contract tests
|
||||
"Fraud\nDetection"->"Fraud\nDetection\nBuild": run the build
|
||||
"Fraud\nDetection\nBuild"->"SCC Plugin": generate tests\nstubs \nand stubs artifact \n(e.g. stubs-jar)
|
||||
"SCC Plugin"->"Fraud\nDetection\nBuild": tests and stubs generated
|
||||
"Fraud\nDetection\nBuild"->"Fraud\nDetection\nBuild": run tests
|
||||
"Fraud\nDetection\nBuild"->"Fraud\nDetection": all the tests passed!
|
||||
"Fraud\nDetection"->"Fraud\nDetection": commit and push changes
|
||||
"Fraud\nDetection"->"CI": commit pushed!\nTriggers the build
|
||||
"CI"->"Stub Storage": build successful,\nupload artifacts
|
||||
----
|
||||
|
||||
[[getting-started-cdc-producer-pr]]
|
||||
=== Taking over the Pull Request
|
||||
|
||||
As a reminder, the following listing shows the initial implementation:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=server_api,indent=0]
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=initial_impl,indent=0]
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Then you can run the following commands:
|
||||
|
||||
====
|
||||
[source,bash,indent=0]
|
||||
----
|
||||
$ git checkout -b contract-change-pr master
|
||||
$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr
|
||||
----
|
||||
====
|
||||
|
||||
You must add the dependencies needed by the autogenerated tests, as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/pom.xml[tags=verifier_test_dependencies,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
In the configuration of the Maven plugin, you must pass the `packageWithBaseClasses` property, as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/pom.xml[tags=contract_maven_plugin,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
IMPORTANT: This example uses "`convention-based`" naming by setting the
|
||||
`packageWithBaseClasses` property. Doing so means that the two last packages combine to
|
||||
make the name of the base test class. In our case, the contracts were placed under
|
||||
`src/test/resources/contracts/fraud`. Since you do not have two packages starting from
|
||||
the `contracts` folder, pick only one, which should be `fraud`. Add the `Base` suffix and
|
||||
capitalize `fraud`. That gives you the `FraudBase` test class name.
|
||||
|
||||
All the generated tests extend that class. Over there, you can set up your Spring Context
|
||||
or whatever is necessary. In this case, you should use https://github.com/rest-assured/rest-assured[Rest Assured MVC] to
|
||||
start the server side `FraudDetectionController`. The following listing shows the
|
||||
`FraudBase` class:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/src/test/java/com/example/fraud/FraudBase.java[]
|
||||
----
|
||||
====
|
||||
|
||||
Now, if you run the `./mvnw clean install`, you get something like the following output:
|
||||
|
||||
====
|
||||
[source,bash,indent=0]
|
||||
----
|
||||
Results :
|
||||
|
||||
Tests in error:
|
||||
ContractVerifierTest.validate_shouldMarkClientAsFraud:32 » IllegalState Parsed...
|
||||
----
|
||||
====
|
||||
|
||||
This error occurs because you have a new contract from which a test was generated, and it
|
||||
failed since you have not implemented the feature. The auto-generated test would look
|
||||
like the following test method:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Test
|
||||
public void validate_shouldMarkClientAsFraud() throws Exception {
|
||||
// given:
|
||||
MockMvcRequestSpecification request = given()
|
||||
.header("Content-Type", "application/vnd.fraud.v1+json")
|
||||
.body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");
|
||||
|
||||
// when:
|
||||
ResponseOptions response = given().spec(request)
|
||||
.put("/fraudcheck");
|
||||
|
||||
// then:
|
||||
assertThat(response.statusCode()).isEqualTo(200);
|
||||
assertThat(response.header("Content-Type")).matches("application/vnd.fraud.v1.json.*");
|
||||
// and:
|
||||
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
|
||||
assertThatJson(parsedJson).field("['fraudCheckStatus']").matches("[A-Z]{5}");
|
||||
assertThatJson(parsedJson).field("['rejection.reason']").isEqualTo("Amount too high");
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
If you used the Groovy DSL, you can see that all the `producer()` parts of the Contract that were present in the
|
||||
`value(consumer(...), producer(...))` blocks got injected into the test.
|
||||
If you use YAML, the same applies for the `matchers` sections of the `response`.
|
||||
|
||||
Note that, on the producer side, you are also doing TDD. The expectations are expressed
|
||||
in the form of a test. This test sends a request to our own application with the URL,
|
||||
headers, and body defined in the contract. It also expects precisely defined values
|
||||
in the response. In other words, you have the `red` part of `red`, `green`, and
|
||||
`refactor`. It is time to convert the `red` into the `green`.
|
||||
|
||||
[[getting-started-cdc-producer-impl]]
|
||||
=== Write the Missing Implementation
|
||||
|
||||
Because you know the expected input and expected output, you can write the missing
|
||||
implementation as follows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=server_api,indent=0]
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=new_impl,indent=0]
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=initial_impl,indent=0]
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
When you run `./mvnw clean install` again, the tests pass. Since the Spring Cloud
|
||||
Contract Verifier plugin adds the tests to the `generated-test-sources`, you can
|
||||
actually run those tests from your IDE.
|
||||
|
||||
[[getting-started-cdc-producer-deploy]]
|
||||
=== Deploying Your Application
|
||||
|
||||
Once you finish your work, you can deploy your changes. To do so, you must first merge the
|
||||
branch by running the following commands:
|
||||
|
||||
====
|
||||
[source,bash,indent=0]
|
||||
----
|
||||
$ git checkout master
|
||||
$ git merge --no-ff contract-change-pr
|
||||
$ git push origin master
|
||||
----
|
||||
====
|
||||
|
||||
Your CI might run a command such as `./mvnw clean deploy`, which would publish both the
|
||||
application and the stub artifacts.
|
||||
|
||||
[[getting-started-cdc-consumer-final]]
|
||||
== Consumer Side (Loan Issuance), Final Step
|
||||
|
||||
As a developer of the loan issuance service (a consumer of the Fraud Detection server), you need to:
|
||||
|
||||
- Merge our feature branch to `master`
|
||||
- Switch to online mode of working
|
||||
|
||||
The following UML diagram shows the final state of the process:
|
||||
|
||||
[plantuml, getting-started-cdc-client-final, png]
|
||||
----
|
||||
"Loan\nIssuance"->"Loan\nIssuance": merge the\nfeature branch\nto master branch
|
||||
"Loan\nIssuance"->"Loan\nIssuance": setup SCC Stub Runner\nto fetch stubs\nfrom Stub Storage
|
||||
"Loan\nIssuance"->"LI\nSCC\nStub Runner": start stubs\nof FD from\nStub Storage
|
||||
"LI\nSCC\nStub Runner"->"Stub Storage": find stubs of [FD]
|
||||
"Stub Storage"->"LI\nSCC\nStub Runner": stubs of [FD] found
|
||||
"LI\nSCC\nStub Runner"->"FD stub": run stubs of [FD]
|
||||
"FD stub"->"LI\nSCC\nStub Runner": [FD] stub is running
|
||||
"LI\nSCC\nStub Runner"->"Loan\nIssuance": stubs running and ready for the test
|
||||
"Loan\nIssuance"->"Loan\nIssuance": run a test
|
||||
"Loan\nIssuance"->"FD stub": the test\nsends a request\nto the running stub
|
||||
"FD stub"->"Loan\nIssuance": stub responds successfuly
|
||||
"Loan\nIssuance"->"Loan\nIssuance": the test passes successfully
|
||||
----
|
||||
|
||||
[[getting-started-cdc-consumer-final-merge]]
|
||||
=== Merging a Branch to Master
|
||||
|
||||
The following commands show one way to merge a branch into master with Git:
|
||||
|
||||
====
|
||||
[source,bash,indent=0]
|
||||
----
|
||||
$ git checkout master
|
||||
$ git merge --no-ff contract-change-pr
|
||||
----
|
||||
====
|
||||
|
||||
[[getting-started-cdc-consumer-final-online]]
|
||||
=== Working Online
|
||||
|
||||
Now you can disable the offline work for Spring Cloud Contract Stub Runner and indicate
|
||||
where the repository with your stubs is located. At this moment, the stubs of the server
|
||||
side are automatically downloaded from Nexus/Artifactory. You can set the value of
|
||||
`stubsMode` to `REMOTE`. The following code shows an example of
|
||||
achieving the same thing by changing the properties:
|
||||
|
||||
====
|
||||
[source,yaml,indent=0]
|
||||
----
|
||||
include:../:{introduction_url}/samples/standalone/dsl/http-client/src/test/resources/application-test-repo.yaml[]
|
||||
----
|
||||
====
|
||||
|
||||
That's it. You have finished the tutorial.
|
||||
|
||||
Reference in New Issue
Block a user