您可以在此处https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/master/producer_proto 的生产者端和在消费者端https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/master/consumer_proto 中查看使用带有 Spring Cloud 合约的 protobuffers 的示例
这个想法是将内容视为二进制。假设我将请求和响应以二进制格式存储在 .bin 文件中。然后我可以创建以下合约
package contracts.beer.rest
import org.springframework.cloud.contract.spec.Contract
Contract.make {
description("""
Represents a successful scenario of getting a beer
```
given:
client is old enough
when:
he applies for a beer
then:
we'll grant him the beer
```
""")
request {
method 'POST'
url '/check'
body(fileAsBytes("PersonToCheck_old_enough.bin"))
headers {
contentType("application/x-protobuf")
}
}
response {
status 200
body(fileAsBytes("Response_old_enough.bin"))
headers {
contentType("application/x-protobuf")
}
}
}
拥有这样的控制器
@RestController
public class ProducerController {
private final PersonCheckingService personCheckingService;
public ProducerController(PersonCheckingService personCheckingService) {
this.personCheckingService = personCheckingService;
}
@RequestMapping(value = "/check",
method=RequestMethod.POST,
consumes="application/x-protobuf",
produces="application/x-protobuf")
public Beer.Response check(@RequestBody Beer.PersonToCheck personToCheck) {
//remove::start[]
if (this.personCheckingService.shouldGetBeer(personToCheck)) {
return Beer.Response.newBuilder().setStatus(Beer.Response.BeerCheckStatus.OK).build();
}
return Beer.Response.newBuilder().setStatus(Beer.Response.BeerCheckStatus.NOT_OK).build();
//remove::end[return]
}
}
interface PersonCheckingService {
Boolean shouldGetBeer(Beer.PersonToCheck personToCheck);
}
还有这样一个用于生成测试的基类(我假设你已经设置了合约插件)
package com.example;
//remove::start[]
import io.restassured.module.mockmvc.RestAssuredMockMvc;
//remove::end[]
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BeerRestBase.Config.class)
public abstract class BeerRestBase {
@Autowired
WebApplicationContext context;
//remove::start[]
@Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(this.context);
}
// remove::end[]
@Configuration
@EnableAutoConfiguration
@Import({ ProtoConfiguration.class, ProducerController.class })
static class Config {
@Bean
PersonCheckingService personCheckingService() {
return argument -> argument.getAge() >= 20;
}
}
}
将导致正确的测试和存根生成。查看上述示例了解具体的实现细节。
在消费者方面,您可以获取存根并针对它们运行测试
package com.example;
import org.assertj.core.api.BDDAssertions;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.stubrunner.junit.StubRunnerRule;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class ProtoTest {
@Autowired
RestTemplate restTemplate;
int port;
@Rule
public StubRunnerRule rule = new StubRunnerRule()
.downloadStub("com.example", "beer-api-producer-proto")
.stubsMode(StubRunnerProperties.StubsMode.LOCAL);
@Before
public void setupPort() {
this.port = this.rule.findStubUrl("beer-api-producer-proto").getPort();
}
@Test
public void should_give_me_a_beer_when_im_old_enough() throws Exception {
Beer.Response response = this.restTemplate.postForObject(
"http://localhost:" + this.port + "/check",
Beer.PersonToCheck.newBuilder().setAge(23).build(), Beer.Response.class);
BDDAssertions.then(response.getStatus()).isEqualTo(Beer.Response.BeerCheckStatus.OK);
}
@Test
public void should_reject_a_beer_when_im_too_young() throws Exception {
Beer.Response response = this.restTemplate.postForObject(
"http://localhost:" + this.port + "/check",
Beer.PersonToCheck.newBuilder().setAge(17).build(), Beer.Response.class);
response = response == null ? Beer.Response.newBuilder().build() : response;
BDDAssertions.then(response.getStatus()).isEqualTo(Beer.Response.BeerCheckStatus.NOT_OK);
}
}
再次,请查看具体示例以了解实施细节。