先輩「MockMvcでJSONPathを使おうと思ってて…」
@takkii1010 「…?」
となったので、以下を簡単にまとめたい。
Data may be interactively found and extracted out of JSON structures on the client without special scripting.
JSONPath expressions always refer to a JSON structure in the same way as XPath expression are used in combination with an XML document.
min()
,
max()
,
avg()
,
stddev()
,
length()
,
sum()
があります。
length()
は
$.phoneNumbers.length
とあまり深く考えずに使えますが、他の関数が少し厄介。
こんな感じのJSONの場合…
"id" : 1 , "name" : "スクエ…" , "products" : [ "name" : "FINAL FANTASY VII" , "price" : 1572 "name" : "FINAL FANTASY X" , "price" : 8800 "name" : "FINAL FANTASY XV" , "price" : 8800
一度取り出してから、処理を行う必要があるらしい。
https://oboe2uran.hatenablog.com/entry/2018/05/06/131634
MockMvcを使う場合は以下で動きました。
val jsonObject = mockMvc.perform(get("/gameCompanies/1"))
.andExpect(status().isOk())
.andReturn().response.contentAsString
val priceList: List<Int> = JsonPath.read( jsonObject, "\$.products[*].price")
val min: Int = JsonPath.read(priceList, "\$.min()")
val max: Int = JsonPath.read(priceList, "\$.max()")
val sum: Int = JsonPath.read(priceList, "\$.sum()")
val stddev: Int = JsonPath.read(priceList, "\$.stddev()")
assertThat(min).isEqualTo(1572)
assertThat(max).isEqualTo(8800)
assertThat(sum).isEqualTo(19172)
assertThat(stddev).isEqualTo(3407)
3. MockMvcでの使い方
@RunWith(SpringRunner::class)
@WebMvcTest(GameCompanyController::class)
class GameCompanyControllerTest {
@Autowired
lateinit var mockMvc: MockMvc
@MockBean
lateinit var gameCompanyRepository: GameCompanyRepositoryInterface
@Test
fun getGameCompanyTest() {
val expectedGameCompany = GameCompany(
id = 1L,
name = "スクエ…",
products = listOf(
Product("FINAL FANTASY VII", BigDecimal(1572)),
Product("FINAL FANTASY X", BigDecimal(8800)),
Product("FINAL FANTASY XV", BigDecimal(8800))
`when`(gameCompanyRepository.fetchGameCompany(any())).thenReturn(expectedGameCompany)
mockMvc.perform(get("/gameCompanies/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("\$.id").value(1))
.andExpect(jsonPath("\$.name").value("スクエ…"))
.andExpect(jsonPath("\$.products[0].name").value("FINAL FANTASY VII"))
.andExpect(jsonPath("\$.products[0].price").value(1572))
.andExpect(jsonPath("\$.products[1].name").value("FINAL FANTASY X"))
.andExpect(jsonPath("\$.products[1].price").value(8800))
.andExpect(jsonPath("\$.products[2].name").value("FINAL FANTASY XV"))
.andExpect(jsonPath("\$.products[2].price").value(8800))
Kotlinの indices
使うとこんな感じ。
mockMvc.perform(get("/gameCompanies/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(expectedGameCompany.products)
private fun ResultActions.andExpect(products: List<Product>): ResultActions {
for (index in products.indices) {
andExpect(jsonPath("\$.products[$index].name").value(products[index].name))
.andExpect(jsonPath("\$.products[$index].price").value(products[index].price))
return this
他にも、 Matchers
のisを使用しても動きます。
mockMvc.perform(get("/gameCompanies/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("\$.id", `is`(1)))
ただ、少し古い書き方のようなので、 value
で書くのが良さそう。
参考にしたサイト一覧
https://goessner.net/articles/JsonPath/
https://www.techscore.com/tech/XML/XPath/XPath1/xpath01.html/
https://github.com/json-path/JsonPath
http://oboe2uran.hatenablog.com/entry/2018/05/06/131634