mad*_*ngi 6 java resttemplate spring-boot mockrestserviceserver
我有一个@ComponentDataClientImpl,它使用RestTemplate. API端点具有查询参数,这些参数在调用时传递RestTemplate。有一个@RestClientTestTest 类DataApiClientImplTest测试DataClientImpl,使用 模拟 REST 调用MockRestServiceServer。
在测试方法中,我想验证 API 调用中是否使用了正确的端点路径和查询参数(特别是名称)。使用MockRestRequestMatchers.requestTo()和MockRestRequestMatchers.queryParam()方法进行验证。
MockRestRequestMatchers.requestTo()运行测试时失败。它似乎将包含查询字符串的实际 url 与不带查询字符串的预期 url(传递给MockRestRequestMatchers.requestTo()method.
在pom中,我正在使用
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version>
<relativePath/>
</parent>
Run Code Online (Sandbox Code Playgroud)
代码如下。
@RestClientTest(DataApiClientImpl.class)
@AutoConfigureWebClient(registerRestTemplate = true)
class DataApiClientImplTest {
private static final String OBJECT_ID_URI = "http://localhost:8080/testBucketId/objects/test-obj-id";
@Autowired
private DataApiClientImpl dataApiClientImpl;
@Autowired
private MockRestServiceServer mockRestServiceServer;
@Test
void testApiCall() {
mockRestServiceServer.expect(MockRestRequestMatchers.requestTo(OBJECT_ID_URI))
.andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
.andExpect(MockRestRequestMatchers.queryParam("keyid", CoreMatchers.anything()))
.andExpect(MockRestRequestMatchers.queryParam("token", CoreMatchers.anything()))
.andRespond(MockRestResponseCreators.withSuccess("dummy", MediaType.APPLICATION_JSON));
String response = dataApiClientImpl.getItem("asdf12345", "test-obj-id");
Assertions.assertThat(response).isNotNull();
}
}
Run Code Online (Sandbox Code Playgroud)
错误是
java.lang.AssertionError: Unexpected request expected:<http://localhost:8080/testBucketId/objects/test-obj-id> but was:<http://localhost:8080/testBucketId/objects/test-obj-id?token=asdf12345&keyid=testKeyId>
Expected :http://localhost:8080/testBucketId/objects/test-obj-id
Actual :http://localhost:8080/testBucketId/objects/test-obj-id?token=asdf12345&keyid=testKeyId
Run Code Online (Sandbox Code Playgroud)
我可以将 Matcher 传递给requestTo()方法。MockRestRequestMatchers.queryParam()但是,当必须使用 Matcher 验证带有查询参数的整个 url 时,该方法有什么用呢?
一种选择是使用startsWith Matcher MockRestRequestMatchers.requestTo(CoreMatchers.startsWith(OBJECT_ID_URI))。但它与精确匹配验证不同。
尝试使用其他重载版本的MockRestRequestMatchers.requestTo()方法。他们的行为都一样。
是否有其他更好的方法来执行精确的端点路径仅匹配。
我没有包含测试主题类代码,DataClientImpl因为我认为它超出了问题的范围,但如果需要,我可以在此处添加。
以防万一有人偶然发现这个线程。答案很简单,但添加更多我有趣的发现的细节可能对其他人有帮助。
TL;DR 答案:
使用mockRestServiceServer.expect(request -> assertEquals(OBJECT_ID_URI, request.getURI().getPath()))
有趣的发现:
看来,它是根据设计MockRestRequestMatchers.requestTo(String uri)与 uri(包括查询字符串)进行匹配的。以下是该方法的实现摘录,
public static RequestMatcher requestTo(String expectedUri) {
Assert.notNull(expectedUri, "'uri' must not be null");
return request -> assertEquals("Request URI", expectedUri, request.getURI().toString());
}
Run Code Online (Sandbox Code Playgroud)
我所说的设计的意思是,根据定义是 RFC 2396 和 RFC 2732 的实现。下面是javadocjava.net.URI的摘录java.net.URI
表示统一资源标识符 (URI) 引用。除了下面提到的一些细微偏差之外,此类的实例表示 RFC 2396 定义的 URI 引用:统一资源标识符 (URI):通用语法,并由 RFC 2732:URL 中的文字 IPv6 地址格式进行修订。
我还没有完全阅读 RFC,但 javadoc 在URI 语法和组件下进一步提到
分层的 URI 需要根据语法进一步解析
[方案:][//权限][路径][?查询][#fragment]
这表明URI不仅仅是请求路径,而是包括所有这些部分。因此,断言方法RequestMatcher返回的RequestMatchers.requestTo(String uri)结果如下
assertEquals("Request URI", expectedUri, request.getURI().toString())
Run Code Online (Sandbox Code Playgroud)
将包括查询字符串的实际 URI 与预期 URI 进行比较。当期望的 uri 字符串仅是路径而没有查询字符串并且实际请求包含查询参数时,断言失败。
因此,我的意图是仅匹配方法中的请求路径expect(),因为查询参数是通过后续andExpect()方法断言的,可以按如下方式实现
mockRestServiceServer.expect(request -> assertEquals(OBJECT_ID_URI, request.getURI().getPath()))
Run Code Online (Sandbox Code Playgroud)
使用java.net.URI#getPath()方法,仅给出实际 URI 的路径部分。
这里的另一个重要点是,它RequestMatcher是一个带有方法的功能接口
void match(ClientHttpRequest request) throws IOException, AssertionError;
Run Code Online (Sandbox Code Playgroud)
lambda 表达式可以传递给以参数为参数且不返回任何内容expect()的方法(从技术上讲)。ClientHttpRequestvoid
完整的测试方法如下并且按预期工作
@Test
void testApiCall() {
mockRestServiceServer.expect(request -> assertEquals(OBJECT_ID_URI, request.getURI().getPath()))
.andExpect(MockRestRequestMatchers.queryParam("keyid", CoreMatchers.anything()))
.andExpect(MockRestRequestMatchers.queryParam("token", CoreMatchers.anything()))
.andRespond(MockRestResponseCreators.withSuccess("dummy", MediaType.APPLICATION_JSON));
String response = dataApiClientImpl.getItem("asdf12345", "test-obj-id");
Assertions.assertThat(response).isNotNull();
}
Run Code Online (Sandbox Code Playgroud)
我最近遇到了类似的问题,没有找到任何内置匹配器/方法的解决方案。我最终编写了自己的 Matcher 实现,如下所示:
public class RequestPathMatcher implements RequestMatcher {
private final String expectedRequestPath;
public static RequestPathMatcher of(String expectedRequestPath) {
return new RequestContainsUriMatcher(expectedRequestPath);
}
private RequestPathMatcher(String expectedRequestPath) {
this.expectedRequestPath = expectedRequestPath;
}
@Override
public void match(ClientHttpRequest clientHttpRequest) throws AssertionError {
final var actualRequestPath = clientHttpRequest.getURI().toString();
var matchResult = false;
// your match logic here
assertTrue(matchResult , String.format("Expected %s to have request path%s", actualRequestPath, expectedRequestPath));
}
}
Run Code Online (Sandbox Code Playgroud)
你可以像这样使用它:
mockServer
.expect(RequestPathMatcher.of(requestPath))
.andRespond(...)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3618 次 |
| 最近记录: |