Spring Cloud Contract简单入门

发布于:2022-12-18 ⋅ 阅读:(537) ⋅ 点赞:(0)

一、背景

1、概述(来着官网的一段介绍)

Spring Cloud Contract 是一个包含解决方案的总括项目,可帮助用户成功实施消费者驱动的合同方法。目前 Spring Cloud Contract 由 Spring Cloud Contract Verifier 项目组成。

Spring Cloud Contract Verifier 是一种工具,它支持基于 JVM 的应用程序的消费者驱动契约 (CDC) 开发。它附带了用 Groovy 或 YAML 编写的合同定义语言 (DSL)。

Spring Cloud Contract

2、自我理解:

简单点来说就是提前设置一个已知结果,消费者调用时直接返回该结果,该结果在消费者的程序处理中是否正确,来测试消费者程序。

3、解决了哪些痛点

目前很多系统都采用了微服务的开发模式,随着项目规模的不断扩大,服务数量越来越多,服务之间的依赖也越来越密切。不同的团队负责不同的服务开发,其中某个服务发生变动,很多服务也是需要进行修改,这将需要大量的沟通成本和代码修改时间成本。

4、目前常见测试中:

单元测试无法测试服务间的连接或接口调用正确性。

API接口测试是针对业务接口进行,主要是测试接口实现是否完整,且需要依赖具体真实服务。

集成测试无疑是最佳的,但是需要依赖具体真实服务,且各个服务都需要同时进行测试,其效率很慢。

契约测试,无需依赖具体真实服务,测试服务间的连接或接口调用正确性 。测试效率快。

总体来说,契约测试是一个介于单元测试和集成测试的一个阶段,他关注的细粒度比单元测试更粗,但是又无法取代集成测试。

二、实现

目前官网和很多博客文章都介绍了,远程库使用契约测试的方法。我这边就介绍一下本地库实现方法。基本一致,只不过读取jar是读取本地。

服务提供者

在服务端编写一个简单的接口,判断数字是否为负数。

@RestController
public class NumberController {

  @GetMapping("/value/number")
  public String isMinus(@RequestParam("number") Integer number) {
    return number < 0 ? "true" : "false";
  }
}

pom文件

<testFramework>JUNIT</testFramework>,不加这个可能无法进行install。

 <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Hoxton.SR12</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-contract-verifier</artifactId>
      <version>3.1.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>

    <plugins>

      <plugin>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-contract-maven-plugin</artifactId>
        <version>3.1.1</version>
        <extensions>true</extensions>
        <configuration>
          <testFramework>JUNIT</testFramework>
          <baseClassForTests>com.lin.test.BaseTestClass</baseClassForTests>
        </configuration>
      </plugin>

      <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          <version>2.3.12.RELEASE</version>
          <configuration>
            <mainClass> com.lin.test.MainApplication</mainClass>
          </configuration>
          <executions>
            <execution>
              <goals>
                <goal>repackage</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
    </plugins>
  </build>

接下来需要新建基础测试类以及存根,注意存放目录,如图所示:

测试类

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@DirtiesContext
@AutoConfigureMessageVerifier
public class BaseTestClass {
  @Autowired
  private NumberController numberController;

  @Before
  public void setup() {
    StandaloneMockMvcBuilder standaloneMockMvcBuilder = MockMvcBuilders.standaloneSetup(numberController);
    RestAssuredMockMvc.standaloneSetup(standaloneMockMvcBuilder);
  }
}

存根(groovy文件)

import org.springframework.cloud.contract.spec.Contract

Contract.make {
    description "should return true when number input is minus"
    request {
        method GET()
        url("/value/number") {
            queryParameters {
                parameter("number", "-1")
            }
        }
    }
    response {
        body("true")
        status 200
    }
}
import org.springframework.cloud.contract.spec.Contract

Contract.make {
    description "should return false when number input not minus"
    request {
        method GET()
        url("/value/number") {
            queryParameters {
                parameter("number", "1")
            }
        }
    }
    response {
        body("false")
        status 200
    }
}

到此服务提供者编写完成,我们接下来进行mvn clean install 就会生成测试类

 构建还会在我们的本地Maven存储库中添加存根jar。

 服务消费者

@RestController
public class BasicMathController {
	@Autowired
	private RestTemplate restTemplate;

	@GetMapping("/calculate")
	public String checkOddAndEven(@RequestParam("number") Integer number) {
		HttpHeaders httpHeaders = new HttpHeaders();
		httpHeaders.add("Content-Type", "application/json");

		ResponseEntity<String> responseEntity = restTemplate.exchange(
				"http://localhost:8090/value/number?number=" + number, HttpMethod.GET,
				new HttpEntity<>(httpHeaders), String.class);

		return responseEntity.getBody();
	}
}

pom文件

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Hoxton.SR12</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-contract-wiremock</artifactId>
      <version>3.1.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
      <version>3.1.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.example</groupId>
      <artifactId>provider-test</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>

   <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>2.3.12.RELEASE</version>
        <configuration>
          <!--指定启动类-->
          <mainClass> com.lin.test.MainApplication</mainClass>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

存根运行器

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
@AutoConfigureMockMvc
@AutoConfigureJsonTesters
@AutoConfigureStubRunner(ids = {"org.example:provider-test:+:stubs:8090"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL)
public class BasicMathControllerIntegrationTest {

  @Autowired
  private MockMvc mockMvc;

  @Test
  public void given_WhenPassEvenNumberInQueryParam_ThenReturnEven() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/calculate?number=2").contentType(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk()).andExpect(content().string("Even"));
  }
}
@AutoConfigureStubRunner这个注解非常重要,这里选择的是本地存根。
之前以为这样就完事了,调用时报错了。找不到对应maven存储库,找到了默认的。

 需要进行设置参数:

org.apache.maven.user-settings

 到此结束,文章有些粗糙,有哪里不对的,欢迎指正。