Draft support for WebTestClient (#514)

* Draft support for WebTestClient
* Share some common code between MockMvc and WebTestClient
* Fix co-ordinates of server stubs
* Fix project names in Gradle
This commit is contained in:
Dave Syer
2018-01-12 14:12:31 +00:00
committed by Marcin Grzejszczak
parent 74b843d76b
commit 92848125b9
67 changed files with 3388 additions and 214 deletions

View File

@@ -0,0 +1,33 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>stubs</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/java</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>**/model/Fraud*.*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/classes</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>**/model/Fraud*.*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/snippets/stubs</directory>
<outputDirectory>META-INF/${project.groupId}/${project.artifactId}/${project.version}/mappings</outputDirectory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,17 @@
package com.example.fraud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,45 @@
package com.example.fraud;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.fraud.model.FraudCheck;
import com.example.fraud.model.FraudCheckResult;
import com.example.fraud.model.FraudCheckStatus;
import java.math.BigDecimal;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
@RestController
public class FraudDetectionController {
private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json";
private static final String NO_REASON = null;
private static final String AMOUNT_TOO_HIGH = "Amount too high";
private static final BigDecimal MAX_AMOUNT = new BigDecimal("5000");
// tag::server_api[]
@RequestMapping(
value = "/fraudcheck",
method = PUT,
consumes = FRAUD_SERVICE_JSON_VERSION_1,
produces = FRAUD_SERVICE_JSON_VERSION_1)
public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
// end::server_api[]
// tag::new_impl[]
if (amountGreaterThanThreshold(fraudCheck)) {
return new FraudCheckResult(FraudCheckStatus.FRAUD, AMOUNT_TOO_HIGH);
}
// end::new_impl[]
// tag::initial_impl[]
return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
// end::initial_impl[]
}
private boolean amountGreaterThanThreshold(FraudCheck fraudCheck) {
return MAX_AMOUNT.compareTo(fraudCheck.getLoanAmount()) < 0;
}
}

View File

@@ -0,0 +1,29 @@
package com.example.fraud.model;
import java.math.BigDecimal;
public class FraudCheck {
private String clientId;
private BigDecimal loanAmount;
public FraudCheck() {
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public BigDecimal getLoanAmount() {
return loanAmount;
}
public void setLoanAmount(BigDecimal loanAmount) {
this.loanAmount = loanAmount;
}
}

View File

@@ -0,0 +1,32 @@
package com.example.fraud.model;
public class FraudCheckResult {
private FraudCheckStatus fraudCheckStatus;
private String rejectionReason;
public FraudCheckResult() {
}
public FraudCheckResult(FraudCheckStatus fraudCheckStatus, String rejectionReason) {
this.fraudCheckStatus = fraudCheckStatus;
this.rejectionReason = rejectionReason;
}
public FraudCheckStatus getFraudCheckStatus() {
return fraudCheckStatus;
}
public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) {
this.fraudCheckStatus = fraudCheckStatus;
}
public String getRejectionReason() {
return rejectionReason;
}
public void setRejectionReason(String rejectionReason) {
this.rejectionReason = rejectionReason;
}
}

View File

@@ -0,0 +1,5 @@
package com.example.fraud.model;
public enum FraudCheckStatus {
OK, FRAUD
}

View File

@@ -0,0 +1 @@
server.port=0

View File

@@ -0,0 +1,78 @@
package com.example.fraud;
import java.math.BigDecimal;
import com.example.fraud.model.FraudCheck;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.BodyInserters;
import static org.springframework.cloud.contract.wiremock.restdocs.WireMockWebTestClient.verify;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureWebTestClient
@AutoConfigureJsonTesters
@DirtiesContext
public class StubGeneratorTests {
@Autowired
private WebTestClient client;
private JacksonTester<FraudCheck> json;
@Before
public void setup() {
ObjectMapper objectMappper = new ObjectMapper();
// Possibly configure the mapper
JacksonTester.initFields(this, objectMappper);
}
@Test
public void shouldMarkClientAsFraud() throws Exception {
FraudCheck fraudCheck = new FraudCheck();
fraudCheck.setClientId("1234567890");
fraudCheck.setLoanAmount(BigDecimal.valueOf(99999.0));
client.put().uri("/fraudcheck")
.contentType(MediaType.valueOf("application/vnd.fraud.v1+json"))
.body(BodyInserters.fromObject(json.write(fraudCheck).getJson()))
.exchange().expectBody().jsonPath("$.fraudCheckStatus").isEqualTo("FRAUD")
.jsonPath("$.rejectionReason").isEqualTo("Amount too high")
.consumeWith(verify().jsonPath("$.clientId")
.jsonPath("$[?(@.loanAmount > 1000)]")
.contentType(MediaType.valueOf("application/vnd.fraud.v1+json"))
.stub("markClientAsFraud"));
}
@Test
public void shouldMarkClientAsNotFraud() throws Exception {
FraudCheck fraudCheck = new FraudCheck();
fraudCheck.setClientId("1234567890");
fraudCheck.setLoanAmount(BigDecimal.valueOf(123.123));
client.put().uri("/fraudcheck")
.contentType(MediaType.valueOf("application/vnd.fraud.v1+json"))
.body(BodyInserters.fromObject(json.write(fraudCheck).getJson()))
.exchange().expectBody().jsonPath("$.fraudCheckStatus").isEqualTo("OK")
.jsonPath("$.rejectionReason").doesNotExist()
.consumeWith(verify().jsonPath("$.clientId")
.jsonPath("$[?(@.loanAmount <= 1000)]")
.contentType(MediaType.valueOf("application/vnd.fraud.v1+json"))
.stub("markClientAsNotFraud"));
}
}