如何在Java编写的Pact契约请求体中注入动态ID?
问题:Pact Provider动态注入作业ID到请求体未生效
我们有一个用于根据ID更新作业的PUT API。测试后需要清理数据,原作业ID被删除后新生成的作业ID会变化,因此尝试从Provider端将动态ID注入请求体,但当前配置未生效,请求仍使用示例ID发起。
Provider类代码
@Slf4j @Provider("Assignments API") @Consumer("LTI-AGS-Tool") //@PactBroker(url = BROKER_PACT_URL, authentication = @PactBrokerAuth(token = "${pactbroker.auth.token}")) @VerificationReports(value = {"console", "markdown"}, reportDir = "target/pacts") class PactProviderLTIAGSIT { private HashMap<String, String> headers = new HashMap<>(); private String updateAssignmentId; private final String SERVICE_TOKEN = "myToken"; @BeforeEach void createTeacherAssignment() { String assignmentBody = createBodyStringForStudentAssignmentSetup(); assignmentBody = assignmentBody.replace("CPWAG", "OTHER_TEXT_RESOURCE"); headers.put("Content-Type", "application/json"); headers.put("Authorization", "myToken"); RequestSpecification rq = Util.getRequestSpecification().baseUri(baseAssignmentUrl).headers(headers); Response response = rq.body(assignmentBody).post(); assertEquals(201, response.getStatusCode()); updateAssignmentId = response.jsonPath().get("assignments[0].refId"); log.info("assignment id is " + updateAssignmentId); } @TestTemplate @ExtendWith(PactVerificationInvocationContextProvider.class) void pactTestTemplate(PactVerificationContext context, HttpRequest request) { request.addHeader("Authorization", SERVICE_TOKEN); logCurlFromPact(context, request); context.verifyInteraction(); } @BeforeEach void before(PactVerificationContext context) { context.setTarget(new HttpsTestTarget(BASE_PACT_TEACHER_ASSIGNMENTS_URL, 443, "")); } @State("Scoring info is passed between ags-tool and assignmentapi") Map<String, Object> getScoringInfo() { Map<String, Object> map = new HashMap<>(); map.put("assignmentId", updateAssignmentId); return map; } }
Consumer契约代码
@ExtendWith(PactConsumerTestExt.class) class PactConsumerSendScoreIT { private final Map<String, String> headers = new HashMap<>(); private final String path = "/v5/assignmentStatus/update"; @Pact(provider = PACT_PROVIDER, consumer = PACT_CONSUMER) public RequestResponsePact scoreConsumerPact(PactDslWithProvider builder) { headers.put("Content-Type", "application/json"); //Body given and returned DslPart body = new PactDslJsonBody() .valueFromProviderState("assignmentId", "assignmentId", "c1ef3bbf-55a2-4638-8f93-22b2916fe085") .stringType("timestamp", DateTime.now().plusHours(3).toString()) .decimalType("scoreGiven", 75.00) .decimalType("scoreMaximum", 100.00) .stringType("comment", "Good work!") .stringType("status", "IN_PROGRESS") .stringType("userId", "c2ef3bbf-55a2-4638-8f93-22b2916fe085") .close(); return builder .given("Scoring info is passed between ags-tool and assignmentapi") .uponReceiving("Scoring info is passed between ags-tool and assignmentapi") .path(path) .method("POST") .body(body) .headers(headers) .willRespondWith() .status(201) .body(body) .toPact(); } @Test @PactTestFor(pactMethod = "scoreConsumerPact", providerName = PACT_PROVIDER, port = "8080", pactVersion = PactSpecVersion.V3) void runTest(MockServer mockServer) { String updateAssignmentId = "c2ef3bbf-55a2-4638-8f93-22b2916fe085"; HashMap<String, Object> map = new HashMap<>(); map.put("timestamp", DateTime.now().plusHours(3).toString()); map.put("scoreGiven", 75.00); map.put("scoreMaximum", 100.00); map.put("comment", "Good work!"); map.put("status", "IN_PROGRESS"); map.put("userId", "c2ef3bbf-55a2-4638-8f93-22b2916fe085"); map.put("assignmentId", updateAssignmentId); //Mock url RequestSpecification rq = Util.getRequestSpecification().baseUri(mockServer.getUrl()).headers(headers); Response response = rq.body(map) .post(path); assertEquals(201, response.getStatusCode()); } }
修复方案
1. 修复Provider中@BeforeEach执行顺序问题
JUnit的@BeforeEach执行顺序不固定,当前两个@BeforeEach可能导致updateAssignmentId未赋值就进入@State方法。合并两个方法,确保先创建作业拿到ID,再设置测试目标:
@BeforeEach void setup(PactVerificationContext context) { // 先创建作业获取动态ID String assignmentBody = createBodyStringForStudentAssignmentSetup(); assignmentBody = assignmentBody.replace("CPWAG", "OTHER_TEXT_RESOURCE"); headers.put("Content-Type", "application/json"); headers.put("Authorization", "myToken"); RequestSpecification rq = Util.getRequestSpecification().baseUri(baseAssignmentUrl).headers(headers); Response response = rq.body(assignmentBody).post(); assertEquals(201, response.getStatusCode()); updateAssignmentId = response.jsonPath().get("assignments[0].refId"); log.info("assignment id is " + updateAssignmentId); // 再设置测试目标 context.setTarget(new HttpsTestTarget(BASE_PACT_TEACHER_ASSIGNMENTS_URL, 443, "")); }
2. 修正请求方法不一致问题
问题描述中提到是PUT API,但Consumer契约和测试代码用的是POST,统一修改为PUT:
- Consumer契约中:
.method("POST")→.method("PUT") - Consumer测试代码中:
.post(path)→.put(path)
3. 手动强制替换请求体中的ID(兜底方案)
如果上述步骤仍不生效,在Provider的测试模板中手动替换请求体里的示例ID:
@TestTemplate @ExtendWith(PactVerificationInvocationContextProvider.class) void pactTestTemplate(PactVerificationContext context, HttpRequest request) { request.addHeader("Authorization", SERVICE_TOKEN); // 替换请求体中的示例ID为动态生成的ID if (request.getBody() != null) { String bodyStr = new String(request.getBody(), StandardCharsets.UTF_8); bodyStr = bodyStr.replace("c1ef3bbf-55a2-4638-8f93-22b2916fe085", updateAssignmentId); request.setBody(bodyStr.getBytes(StandardCharsets.UTF_8)); } logCurlFromPact(context, request); context.verifyInteraction(); }
4. 验证@State方法的返回值
在getScoringInfo方法中添加日志,确认updateAssignmentId不为空:
@State("Scoring info is passed between ags-tool and assignmentapi") Map<String, Object> getScoringInfo() { log.info("Providing assignmentId from state: {}", updateAssignmentId); Map<String, Object> map = new HashMap<>(); map.put("assignmentId", updateAssignmentId); return map; }
内容的提问来源于stack exchange,提问作者Francislainy Campos
相关产品推荐
相关产品推荐

