本指南将引导您完成创建带有合约存根的 Spring REST 应用程序并在另一个 Spring 应用程序中使用该合约的过程。
您将构建的内容
你将设置两个微服务,一个提供其协定,另一个使用此合约,以确保与合约提供程序服务的集成符合规范。如果将来生产者服务的契约发生变化,那么消费者服务的测试将无法捕获潜在的不兼容性。
你需要什么
- 约15分钟
- 最喜欢的文本编辑器或 IDE
- JDK 1.8或以后
- 格拉德尔 4+或梅文 3.2+
- 您也可以将代码直接导入到 IDE 中:
- 弹簧工具套件 (STS)
- 智能理念
- VSCode
如何完成本指南
像大多数春天一样入门指南,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到工作代码。
要从头开始,请继续使用 Gradle 构建.
要跳过基础知识,请执行以下操作:
- 下载并解压缩本指南的源存储库,或使用吉特:
git clone https://github.com/spring-guides/gs-contract-rest.git
- 光盘成
gs-contract-rest/initial
- 跳转到创建合同生产者服务.
完成后,您可以根据 中的代码检查结果。gs-contract-rest/complete
使用 Gradle 构建
使用 Maven 构建
使用 IDE 进行构建
创建合同生产者服务
首先需要创建生成协定的服务。这是一个常规的 Spring 启动应用程序,提供了一个非常简单的 REST 服务。其余服务仅返回 JSON 格式的对象。Person
contract-rest-service/src/main/java/hello/ContractRestServiceApplication.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ContractRestServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ContractRestServiceApplication.class, args);
}
}
创建 REST 服务的协定
REST 服务的协定可以定义为脚本。该合约指定,如果有对url的请求,示例数据,并且表示实体将在内容类型的响应正文中返回。.groovy
GET
/person/1
id=1
name=foo
surname=bee
Person
application/json
contract-rest-service/src/test/resources/contracts/hello/find_person_by_id.groovy
import org.springframework.cloud.contract.spec.Contract
Contract.make {
description "should return person by id=1"
request {
url "/person/1"
method GET()
}
response {
status OK()
headers {
contentType applicationJson()
}
body (
id: 1,
name: "foo",
surname: "bee"
)
}
}
在该阶段,将为 groovy 文件中指定的合约创建自动测试类。这是由 Gradle 或 Maven 中的构建插件分别完成的。自动生成的测试 java 类将扩展 .test
org.springframework.cloud:spring-cloud-contract-gradle-plugin
org.springframework.cloud:spring-cloud-contract-maven-plugin
hello.BaseClass
要在 Maven 中包含插件,您需要添加以下内容:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<baseClassForTests>hello.BaseClass</baseClassForTests>
</configuration>
</plugin>
若要运行测试,还需要在测试范围中包含依赖项。org.springframework.cloud:spring-cloud-starter-contract-verifier
最后,创建用于测试的基类:
contract-rest-service/src/test/java/hello/BaseClass.java
package hello;
import org.junit.jupiter.api.BeforeEach;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import io.restassured.module.mockmvc.RestAssuredMockMvc;
@SpringBootTest(classes = ContractRestServiceApplication.class)
public abstract class BaseClass {
@Autowired PersonRestController personRestController;
@MockBean PersonService personService;
@BeforeEach public void setup() {
RestAssuredMockMvc.standaloneSetup(personRestController);
Mockito.when(personService.findPersonById(1L))
.thenReturn(new Person(1L, "foo", "bee"));
}
}
在此步骤中,执行测试时,测试结果应为 GREEN 表示 REST 控制器与协定一致,并且您具有功能齐全的服务。
检查简单的人员查询业务逻辑
模型类Person.java
contract-rest-service/src/main/java/hello/Person.java
package hello;
class Person {
Person(Long id, String name, String surname) {
this.id = id;
this.name = name;
this.surname = surname;
}
private Long id;
private String name;
private String surname;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
}
服务Bean,它只是在内存中填充几个Person实体,并在询问时返回该实体。PersonService.java
contract-rest-service/src/main/java/hello/PersonService.java
package hello;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Service;
@Service
class PersonService {
private final Map<Long, Person> personMap;
public PersonService() {
personMap = new HashMap<>();
personMap.put(1L, new Person(1L, "Richard", "Gere"));
personMap.put(2L, new Person(2L, "Emma", "Choplin"));
personMap.put(3L, new Person(3L, "Anna", "Carolina"));
}
Person findPersonById(Long id) {
return personMap.get(id);
}
}
RestController bean,当收到具有 id 的人的 REST 请求时调用 bean。PersonRestController.java
PersonService
contract-rest-service/src/main/java/hello/PersonRestController.java
package hello;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
class PersonRestController {
private final PersonService personService;
public PersonRestController(PersonService personService) {
this.personService = personService;
}
@GetMapping("/person/{id}")
public Person findPersonById(@PathVariable("id") Long id) {
return personService.findPersonById(id);
}
}
测试合同-休息-服务应用程序
将类作为 Java 应用程序或 Spring 引导应用程序运行。服务应从端口 开始。ContractRestServiceApplication.java
8000
在浏览器中访问服务,等。http://localhost:8000/person/1
http://localhost:8000/person/2
创建合约使用者服务
准备好合约生产者服务后,现在我们需要创建使用所提供的合约的客户端应用程序。这是一个常规的 Spring 启动应用程序,提供了一个非常简单的 REST 服务。其余服务只是返回一条带有被查询人员姓名的消息,例如 .Hello Anna
contract-rest-client/src/main/java/hello/ContractRestClientApplication.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ContractRestClientApplication {
public static void main(String[] args) {
SpringApplication.run(ContractRestClientApplication.class, args);
}
}
@RestController
class MessageRestController {
private final RestTemplate restTemplate;
MessageRestController(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
@RequestMapping("/message/{personId}")
String getMessage(@PathVariable("personId") Long personId) {
Person person = this.restTemplate.getForObject("http://localhost:8000/person/{personId}", Person.class, personId);
return "Hello " + person.getName();
}
}
创建协定测试
生产者提供的合同应作为简单的春季测试使用。
contract-rest-client/src/test/java/hello/ContractRestClientApplicationTest.java
package hello;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
@SpringBootTest
@AutoConfigureStubRunner(
ids = "com.example:contract-rest-service:0.0.1-SNAPSHOT:stubs:8100",
stubsMode = StubRunnerProperties.StubsMode.LOCAL
)
public class ContractRestClientApplicationTest {
@Test
public void get_person_from_service_contract() {
// given:
RestTemplate restTemplate = new RestTemplate();
// when:
ResponseEntity<Person> personResponseEntity = restTemplate.getForEntity("http://localhost:8100/person/1", Person.class);
// then:
BDDAssertions.then(personResponseEntity.getStatusCodeValue()).isEqualTo(200);
BDDAssertions.then(personResponseEntity.getBody().getId()).isEqualTo(1l);
BDDAssertions.then(personResponseEntity.getBody().getName()).isEqualTo("foo");
BDDAssertions.then(personResponseEntity.getBody().getSurname()).isEqualTo("bee");
}
}
此测试类将加载合约生产者服务的存根,并确保与服务的集成与协定一致。
如果消费者服务的测试和生产者的合同之间的通信有问题,测试将失败,在对生产进行新的更改之前,需要修复问题。
测试协定-休息-客户端应用程序
将类作为 Java 应用程序或 Spring 引导应用程序运行。服务应从端口 开始。ContractRestClientApplication.java
9000
在浏览器中访问服务,等。http://localhost:9000/message/1
http://localhost:9000/message/2
总结
祝贺!你刚刚使用Spring让你的REST服务声明他们的契约,消费者服务与这个契约保持一致。
标签:java,Spring,springframework,contract,存根,应用程序,org,import,id From: https://blog.51cto.com/u_15326439/5968601