第十六節(jié) SCC消費驅(qū)動測試-生產(chǎn)端

spring cloud contract 使用

官方地址

生產(chǎn)端

http spring cloud contract 使用 product

[https://docs.spring.io/spring-cloud-contract]

生產(chǎn)端步驟

https://docs.spring.io/spring-cloud-contract/docs/3.0.2/reference/htmlsingle/#getting-started-first-application-producer

  1. pom輸入
    To start working with Spring Cloud Contract, you can add the Spring Cloud Contract Verifier dependency and plugin to your build file, as the following example shows:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-verifier</artifactId>
    <scope>test</scope>
</dependency>

以下清單顯示了如何添加插件,該插件應(yīng)放在文件的buildplugins部分中:```xml

<plugin>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    <extensions>true</extensions>
    <configuration>
        <!--用于構(gòu)建過程中插件自動生成測試用例的基類,BaseCase使用http基類-->
        <baseClassForTests> com.xzg.test.scc.BaseCase</baseClassForTests>
    </configuration>
</plugin>
  1. 對于HTTPstubs,契約定義了應(yīng)針對給定請求返回的響應(yīng)類型(考慮到HTTP方法底桂,URL腥光,標頭巴帮,狀態(tài)碼等)落包。以下示例顯示了Groovy和YAML中的HTTPstubs協(xié)定:
    契約默認存放在test下resource的contracts目錄下
request:
  method: PUT
  url: /fraudcheck
  body:
    "client.id": 1234567890
    loanAmount: 99999
  headers:
    Content-Type: application/json
  matchers:
    body:
      - path: $.['client.id']
        type: by_regex
        value: "[0-9]{10}"
response:
  status: 200
  body:
    fraudCheckStatus: "FRAUD"
    "rejection.reason": "Amount too high"
  headers:
    Content-Type: application/json;charset=UTF-8
  1. 執(zhí)行命令將生成測試stubs測試類,用于測試肺缕,請求是否正確

mvn clean install
可以看到日志:

....
[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

同時可以在target的generated-test-source目錄下生成測試文件

@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");
}
  • 完成后消費端既可直接使用

使用kafka消息中間件的scc

我們應(yīng)考慮三種主要情況:
方案1:沒有輸入消息會生成輸出消息。輸出消息由應(yīng)用程序內(nèi)部的組件(例如,調(diào)度程序)觸發(fā)同木。
方案2:輸入消息觸發(fā)輸出消息浮梢。
方案3:輸入消息已被使用,并且沒有輸出消息彤路。

測試使用方案2秕硝,

  1. 修改pom支持kafka
  <dependencies>
  <dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>
    <dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka-test</artifactId>
    <scope>test</scope>
    </dependency>
</dependencies>
<!--。洲尊。远豺。-->
<build>
 <plugins>
            <plugin>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-contract-maven-plugin</artifactId>
                <version>${spring-cloud-contract-plugin.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <!--用于構(gòu)建過程中插件自動生成測試用例的基類,BaseCase使用http基類-->
                    <baseClassForTests> com.xzg.test.scc.BaseCase</baseClassForTests>
                    <!--用于構(gòu)建過程中插件自動生成測試用例的基類坞嘀,下面使用test mesage下的契約的基類躯护,用于kafka-->
                    <baseClassMappings>
                        <baseClassMapping>
                            <contractPackageRegex>.*message.*</contractPackageRegex>
                            <baseClassFQN>com.xzg.test.scc.BaseKafkaCase</baseClassFQN>
                        </baseClassMapping>
                    </baseClassMappings>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <!--用于指定自動生成測試類的目錄-->
                    <execution>
                        <id>add-source</id>
                        <phase>generate-test-sources</phase>
                        <goals>
                            <goal>add-test-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.build.directory}/generated-test-sources/contracts/</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
</build>
  1. 編寫契約
# Human readable description
description: Some description
# Label by means of which the output message can be triggered
label: some_label
# input is a message
input:
  messageFrom: kafka_topic
  # has the following body
  messageBody:
    bookName: 'foo'
  # and the following headers
  messageHeaders:
    sample: 'header'
# output message of the contract
outputMessage:
  # destination to which the output message will be sent
  sentTo: kafka_topic
  # the body of the output message
  body:
    bookName: foo
  # the headers of the output message
  headers:
    BOOK-NAME: foo
  1. 測試生成測試

mvn clean install -DTest

  1. 查看編譯后自動生成測試類
    target/generated-test-source/contracts/
package com.xzg.test.scc;

import com.xzg.test.scc.BaseKafkaCase;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import org.junit.Test;
import org.junit.Rule;
import javax.inject.Inject;
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper;
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage;
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging;

import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat;
import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*;
import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;
import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers;
import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes;

@SuppressWarnings("rawtypes")
public class MessageTest extends BaseKafkaCase {
    @Inject ContractVerifierMessaging contractVerifierMessaging;
    @Inject ContractVerifierObjectMapper contractVerifierObjectMapper;

    @Test
    public void validate_inputmessage() throws Exception {
        // given:
            ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
                    "{\"bookName\":\"foo\"}"
                        , headers()
                            .header("sample", "header")
            );

        // when:
            contractVerifierMessaging.send(inputMessage, "kafka_topic");

        // then:
            ContractVerifierMessage response = contractVerifierMessaging.receive("kafka_topic");
            assertThat(response).isNotNull();

        // and:
            assertThat(response.getHeader("BOOK-NAME")).isNotNull();
            assertThat(response.getHeader("BOOK-NAME").toString()).isEqualTo("foo");

        // and:
            DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()));
            assertThatJson(parsedJson).field("['bookName']").isEqualTo("foo");
    }

    @Test
    public void validate_trigger() throws Exception {
        // when:
            trigger();

        // then:
            ContractVerifierMessage response = contractVerifierMessaging.receive("kafka_topic");
            assertThat(response).isNotNull();

        // and:
            assertThat(response.getHeader("BOOK-NAME")).isNotNull();
            assertThat(response.getHeader("BOOK-NAME").toString()).isEqualTo("foo");
            assertThat(response.getHeader("contentType")).isNotNull();
            assertThat(response.getHeader("contentType").toString()).isEqualTo("application/json");

        // and:
            DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()));
            assertThatJson(parsedJson).field("['bookName']").isEqualTo("foo");
    }

}

注意:編譯會自動將生成stub.jar上傳到本地maven倉庫。消費者端使用也是通過拉去本地測試丽涩。如果想要推送到遠程倉庫棺滞,需要單獨修改Spring Cloud Contract Stub Runner properties。另一種方式也可以使用git推送内狸,本地拉去編譯

[示例源碼地址](xiongzhenggang/spring-cloud-contract-example (github.com)
)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末检眯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子昆淡,更是在濱河造成了極大的恐慌锰瘸,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昂灵,死亡現(xiàn)場離奇詭異避凝,居然都是意外死亡,警方通過查閱死者的電腦和手機眨补,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進店門管削,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撑螺,你說我怎么就攤上這事含思。” “怎么了甘晤?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵含潘,是天一觀的道長。 經(jīng)常有香客問我线婚,道長遏弱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任塞弊,我火速辦了婚禮漱逸,結(jié)果婚禮上泪姨,老公的妹妹穿的比我還像新娘。我一直安慰自己饰抒,他們只是感情好肮砾,可當(dāng)我...
    茶點故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著循集,像睡著了一般唇敞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咒彤,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天疆柔,我揣著相機與錄音,去河邊找鬼镶柱。 笑死旷档,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的歇拆。 我是一名探鬼主播鞋屈,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼故觅!你這毒婦竟也來了厂庇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤输吏,失蹤者是張志新(化名)和其女友劉穎权旷,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贯溅,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡拄氯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了它浅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片译柏。...
    茶點故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖姐霍,靈堂內(nèi)的尸體忽然破棺而出鄙麦,到底是詐尸還是另有隱情,我是刑警寧澤镊折,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布黔衡,位于F島的核電站,受9級特大地震影響腌乡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜夜牡,卻給世界環(huán)境...
    茶點故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一与纽、第九天 我趴在偏房一處隱蔽的房頂上張望侣签。 院中可真熱鬧,春花似錦急迂、人聲如沸影所。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猴娩。三九已至,卻和暖如春勺阐,著一層夾襖步出監(jiān)牢的瞬間卷中,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工渊抽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蟆豫,地道東北人。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓懒闷,卻偏偏與公主長得像十减,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子愤估,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,926評論 2 361

推薦閱讀更多精彩內(nèi)容