我需要在集成测试中测试 HTTP 端点。有两种可接受的回应:
我知道如何分别测试每个可接受的响应。示例(Kotlin):
mockMvc.get("/myresource") {
contentType = MediaType.APPLICATION_JSON
accept = MediaType.APPLICATION_JSON
}.andExpect {
status { isOk() }
content { contentType(MediaType.APPLICATION_JSON) }
jsonPath("$.greeting", `is`("Hello SO"))
}
和:
mockMvc.get("/myresource") {
contentType = MediaType.APPLICATION_JSON
accept = MediaType.APPLICATION_JSON
}.andExpect {
status { is5xxServerError() }
}
如何在一次测试中结合两种可接受的回答?
我希望我不需要求助于
RestTemplate
.
爪哇:
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.oneOf;
import static org.hamcrest.Matchers.allOf;
mockMvc.stuff().andExpect(status().is(oneOf(200, 503)))
mockMvc.stuff()
.andExpect(status().is(anyOf(allOf(lessThan(505), greaterThan(500)),
allOf(lessThan(205), greaterThan(200)))));
is()
后面的status()
是这个。我写的不一定是使用 Hamcrest 匹配器(有 lot 可供选择)的最佳方法。此外,您最好自己编写。但这是一般的想法。
不像接受两个状态码那么简单:根据实际状态,可能需要对内容和json路径做进一步的断言。所以这个场景比看起来更复杂,我怀疑内置
ResultMatchers
支持它。该场景还测试了两个 完全不同 结果的事实无助于在 spring-test 项目中添加此类支持。
也就是说,有一些方法可以建立有条件地执行断言的期望:
mockMvc.perform(get("/myresource"))
.andExpect(status().is(anyOf(is(HttpStatus.OK), new CustomMatcher<>("status should be 5xx") {
@Override
public boolean matches(Object status) {
return status instanceof Integer
&& HttpStatus.Series.SERVER_ERROR.equals(HttpStatus.valueOf((int) status).series());
}
})))
.andExpect(mvcResult -> {
if (mvcResult.getResponse().getStatus() == HttpStatus.OK.value()) {
content().contentType(MediaType.APPLICATION_JSON).match(mvcResult);
jsonPath("$.greeting", is("Hello SO")).match(mvcResult);
}
})
这转化为:
根据 MA 和 neofelis 的回答(非常感谢!),我找到了解决方案(在 Kotlin 中)。
可重复使用
CustomMatcher
识别一个5xx状态码:
fun isServerError5xx() = IsServerError5xx()
class IsServerError5xx : CustomMatcher<Any?>("Check if status code is 5xx") {
override fun matches(status: Any?): Boolean =
if (status == null || status !is Int)
false
else
HttpStatus.resolve(status)?.is5xxServerError == true
}
MockMvc
测试示例(使用 MockMvc DSL):
mockMvc.get("/myresource") {
contentType = MediaType.APPLICATION_JSON
accept = MediaType.APPLICATION_JSON
}.andExpect {
status { `is`(anyOf(`is`(HttpStatus.OK.value()), isServerError5xx())) }
match {
if (it.response.status == HttpStatus.OK.value()) {
content { contentType(MediaType.APPLICATION_JSON) }
jsonPath("$.greeting", `is`("Hello SO"))
}
}
}
使用 MockMvc DSL 的相同示例not:
mockMvc.perform(
get(""/myresource"")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(status().`is`(anyOf(`is`(HttpStatus.OK.value()), isServerError5xx())))
.andExpect { mvcResult: MvcResult ->
if (mvcResult.response.status == HttpStatus.OK.value()) {
content().contentType(MediaType.APPLICATION_JSON).match(mvcResult)
jsonPath("$.greeting", `is`("Hello SO"))
}
}