1.使用start.spring.io创建一个“web”项目。在“依赖项”对话框中搜索并添加“web”依赖项,为了后面的契约文件,再加入“Config Client ”和“Contract Stub Runner依赖项。点击“生成”按钮,下载zip,并将其解压缩到计算机上的文件夹中。
2.pom.xml
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>com.example</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> <java.version>11</java.version> <cucumber.version>6.8.1</cucumber.version> <spring-cloud.version>2023.0.0</spring-cloud.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-contract-stub-runner</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-contract-verifier</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-java</artifactId> <version>${cucumber.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-spring</artifactId> <version>${cucumber.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-junit-platform-engine</artifactId> <version>${cucumber.version}</version> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <version>4.1.0</version> <extensions>true</extensions> <configuration> <testFramework>JUNIT5</testFramework> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3 目录结构如下
4 ATMService.feature
代码语言:javascript
复制
功能:验证密码 作为银行储户 我想要在 ATM 上验证密码 以便我可以安全地进行操作 场景:验证密码成功 假如储户拥有一张卡号为"1111222233"的借记卡 并且密码为"123456" 并且储户借记卡账户余额为"100.00"元 当储户将卡插入ATM 并且储户选择查询余额 那么提示储户输入密码 并且输入密码"123456" 那么储户可以看到密码正确的提示 场景:验证密码失败 假如储户拥有一张卡号为"1111222233"的借记卡 并且密码为"123456" 并且储户借记卡账户余额为"100.00"元 当储户将卡插入ATM 并且储户选择查询余额 那么提示储户输入密码 并且输入密码"654321" 那么储户可以看到密码错误的提示
5 先来看看测试文件
代码语言:javascript
复制
MyDemoApplicationTests.java package com.example.ATMService; import io.cucumber.junit.platform.engine.Cucumber; import io.cucumber.spring.CucumberContextConfiguration; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @Cucumber @CucumberContextConfiguration @SpringBootTest class MyDemoApplicationTests { @Test void contextLoads() { } }
VerifyPINStepDefinitions.java
代码语言:javascript
复制
package com.example.ATMService; //mvn spring-cloud-contract:convert;mvn spring-cloud-contract:run //http://localhost:8080/verify_pin/1111222233/123456 import com.example.ATMService.domain.model.Account; import com.example.ATMService.domain.model.DebitCard; import com.example.ATMService.performer.ATM; import com.example.ATMService.performer.Customer; import io.cucumber.java.zh_cn.假如; import io.cucumber.java.zh_cn.当; import io.cucumber.java.zh_cn.那么; import io.cucumber.junit.platform.engine.Cucumber; import org.junit.jupiter.api.Assertions; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest @Cucumber public class VerifyPINStepDefinitions { private final Customer customer = new Customer(); private final ATM atm = new ATM(); @假如("储户拥有一张卡号为\"{int}\"的借记卡") public void 储户拥有一张卡号为_的借记卡(Integer cardIdInteger){ Long cardId = cardIdInteger.longValue(); this.customer.haveCard(new DebitCard(cardId)); } @假如("密码为\"{int}\"") public void 密码为(Integer PIN){ this.customer.setDebitCardPIN(PIN); } @假如("储户借记卡账户余额为\"{double}\"元") public void 储户借记卡账户余额为_元(Double balance){ this.customer.setCardAccount(new Account(balance)); } @假如("ATM已经初始化") public void ATM已经初始化(){ this.atm.init(); } @当("储户将卡插入ATM") public void 储户将卡插入atm(){ this.customer.insertCardToATM(atm); } @当("储户选择查询余额") public void 储户选择查询余额(){ this.customer.queryBalanceOn(atm); } @那么("提示储户输入密码") public void 提示储户输入密码(){ Assertions.assertEquals("Please input PIN:", this.atm.getScreenMessage()); } @那么("输入密码\"{int}\"") public void 输入密码(Integer pin){ this.customer.enterPIN(this.atm, pin); } @那么("储户可以看到密码正确的提示") public void 储户可以看到密码正确的提示(){ Assertions.assertEquals("your PIN is invalid.", this.atm.getScreenMessage()); } @那么("储户可以看到密码错误的提示") public void 储户可以看到密码错误的提示(){ Assertions.assertEquals("your PIN is invalid.", this.atm.getScreenMessage()); } }
com.example.ATMService.domain.model目录下
Account.java
代码语言:javascript
复制
package com.example.ATMService.domain.model; public class Account { private final Double balance; public Account(Double balance) { this.balance = balance; } public double getBalance() { return balance; } }
com.example.ATMService.domain.model目录下
DebitCard.java
代码语言:javascript
复制
package com.example.ATMService.domain.model; public class DebitCard { private Integer PIN= -1; private final Long cardId; private Account account; public DebitCard(Long cardId){ this.cardId = cardId; } public void setPIN(Integer pin){ this.PIN = pin.intValue(); } public void setAccount(Account account) { this.account = account; } public double getBalance() { return this.account.getBalance(); } public boolean verifyPIN(Integer pin) { return this.PIN.intValue()== pin; } public Long getCardID() { return this.cardId; } }
com.example.ATMService.domain.service目录下
DebitCardService.java
代码语言:javascript
复制
package com.example.ATMService.domain.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Map; @Service public class DebitCardService { @Value("${card-service.host}") private String cardServiceHost; public boolean verifyPIN(Long cardID, Integer pin){ RestTemplate template = new RestTemplate(); try{ String requestURL= String.format("http://%s/verify_pin/%d/%d",this.cardServiceHost, cardID,pin); ResponseEntity Map body= entity.getBody(); return "OK".equals(body.get("result"))&&(entity.getStatusCode()== HttpStatus.ACCEPTED); }catch (Exception e) { return false; } } }
com.example.ATMService.performer目录下
ATM.java
代码语言:javascript
复制
package com.example.ATMService.performer; import com.example.ATMService.domain.model.DebitCard; import com.example.ATMService.domain.service.DebitCardService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ATM{ @Autowired private DebitCardService debitCardService = new DebitCardService(); private DebitCard card; private String screenMessage; private boolean verifiedPIN = false; public void init() { this.verifiedPIN = false; this.card = null; this.screenMessage = null; } public void insertCard(DebitCard debitCard) { this.card = debitCard; } public void queryBalance() { if(this.verifiedPIN){ this.screenMessage = String.format("Your balance is: %f", this.card.getBalance()); }else { this.screenMessage = String.format("Please input PIN:"); } } public String getScreenMessage() { return this.screenMessage; } public void enterPlN(Integer pin) { this.verifiedPIN = this.debitCardService.verifyPIN(this.card.getCardID(), pin); if (!this.verifiedPIN) { this.screenMessage ="your PIN is invalid."; }else { this.queryBalance(); } } }
com.example.ATMService.performer目录下
Customer.java
代码语言:javascript
复制
package com.example.ATMService.performer; import com.example.ATMService.domain.model.Account; import com.example.ATMService.domain.model.DebitCard; public class Customer { private DebitCard debitCard; public void haveCard(DebitCard debitCard) { this.debitCard = debitCard; } public void setDebitCardPIN(Integer pin){ this.debitCard.setPIN(pin); } public void setCardAccount(Account account) { this.debitCard.setAccount(account); } public void insertCardToATM(ATM atm){ //atm.reset(); atm.insertCard(this.debitCard); } public void queryBalanceOn(ATM atm) { atm.queryBalance(); } public void enterPIN(ATM atm, Integer pin) { atm.enterPlN(pin); } }
6 用JUnit运行VerifyPINStepDefinitions.Java
7 在/ATMService/src/test/resources/contracts目录下建立
verify_pin.yml
代码语言:javascript
复制
request: method: GET url: /verify_pin/1111222233/123456 response: status: 200 headers: Content-Type: application/json;charset=UTF-8 body: result: "OK"
verify_pin_fail.yml
代码语言:javascript
复制
reguest: method: GET url: /verify_pin/1111222233/654321 respouse: status: 400 headers: Contentlype: Appliction/json;chatset=utf-8 body: result:"Your PlN is apnalnd"
8 运行
代码语言:javascript
复制
mvn spring-cloud-contract:convert&&mvn spring-cloud-contract:run
在浏览器中输入
代码语言:javascript
复制
http://127.0.0.1:8080/verify_pin/1111222233/123456
代码语言:javascript
复制
http://127.0.0.1:8080/verify_pin/1111222233/654321
9.建立另一个Spring Boot card
在/card/target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/ContractVerifierTest.java中产生
代码语言:javascript
复制
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification; import io.restassured.response.ResponseOptions; 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 io.restassured.module.mockmvc.RestAssuredMockMvc.*; @SuppressWarnings("rawtypes") public class ContractVerifierTest { @Test public void validate_verify_pin() throws Exception { // given: MockMvcRequestSpecification request = given(); // when: ResponseOptions response = given().spec(request) .get("/verify_pin/1111222233/123456"); // then: assertThat(response.statusCode()).isEqualTo(200); assertThat(response.header("Content-Type")).isEqualTo("application/json;charset=UTF-8"); // and: DocumentContext parsedJson = JsonPath.parse(response.getBody().asString()); assertThatJson(parsedJson).field("['result']").isEqualTo("OK"); } }
10. 实现卡片controller
代码语言:javascript
复制
package com.example.card.interfaces; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class CardController { @GetMapping("/verify_pin/{id}/{pin}") public ResponseEntityverifyPIN(@PathVariable("id") Long id, @PathVariable("pin") Integer pin) { HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.set("Content-Type", "Application/json;charset-utf-8"); ResponseEntityresponse; if(id==1111222233 && pin==123456) response = new ResponseEntity("{\"result\":\"OK\"}", responseHeaders, HttpStatus.ACCEPTED); else response = new ResponseEntity("{\"result\":\"Your PIN is invalid\"}",responseHeaders,HttpStatus.BAD_REQUEST); return response; } }
在卡片项目中运行
代码语言:javascript
复制
mvn spring-boot:run
启动卡片项目
然后运行amt下的mvn test测试通过
11 我的问题,如何测试在浏览器中输入http://127.0.0.1:8080/verify_pin/1111222233/123456显示pass信息,123456为其他字符显示fail信息。