#kotlin #rest-assured #rest-assured-jsonpath
Вопрос:
Я пишу несколько тестов с использованием rest-assured и его расширений Kotlin для тестирования некоторых простых конечных точек Spring MVC. Я пытаюсь понять, как извлекать ценности.
Одна конечная точка возвращает BookDetailsView
POJO, другая возвращает a Page<BookDetailsView>
(где Page
находится интерфейс, предоставляемый Spring для выполнения подкачки).
BookDetailsView
это действительно простой класс данных Kotlin с одним полем:
data class BookDetailsView(val id: UUID)
Для конечной точки одного объекта у меня есть:
@Test
fun `single object`() {
val details = BookDetailsView(UUID.randomUUID())
whenever(bookDetailsService.getBookDetails(details.id)).thenReturn(details)
val result: BookDetailsView = Given {
mockMvc(mockMvc)
} When {
get("/book_details/${details.id}")
} Then {
statusCode(HttpStatus.SC_OK)
} Extract {
`as`(BookDetailsView::class.java)
}
assertEquals(details.id, result.id)
}
Это работает, как и ожидалось, но попытка применить ту же технику для Page<BookDetailsView>
решения всевозможных задач синтаксического анализа, поскольку Страница-это интерфейс, и даже пытаться использовать PageImpl
ее не совсем просто. В конце концов, я даже на самом деле не забочусь об Page
объекте, я просто забочусь о вложенном списке POJO внутри него.
Я пробовал различные перестановки, такие как приведенный ниже код, чтобы просто взять то, что меня волнует:
@Test
fun `extract nested`() {
val page = PageImpl(listOf(
BookDetailsView(UUID.randomUUID())
))
whenever(bookDetailsService.getBookDetailsPaged(any())).thenReturn(page)
val response = Given {
mockMvc(mockMvc)
} When {
get("/book_details")
} Then {
statusCode(HttpStatus.SC_OK)
body("content.size()", `is`(1))
body("content[0].id", equalTo(page.first().id.toString()))
} Extract {
path<List<BookDetailsView>>("content")
}
println(response[0].javaClass)
}
Последний println
выплевывает class java.util.LinkedHashMap
. Если вместо этого я попытаюсь на самом деле использовать объект, я получу class java.util.LinkedHashMap cannot be cast to class BookDetailsView
. Есть много вопросов и ответов, связанных с этим, и я понимаю, что в конечном счете это проблема, связанная с тем, что базовый синтаксический анализатор JSON не знает, что делать, но я не совсем понимаю:
- Почему «простой» случай разбирается без проблем?
- Разве параметр типа, передаваемый
path()
функции, не должен указывать ей, какой тип использовать? - Что нужно настроить, чтобы второй случай работал, ИЛИ
- Есть ли какой-то другой подход для захвата вложенного объекта, который имел бы больше смысла?
Покопавшись немного в коде, кажется, что в двух случаях на самом деле могут использоваться разные парсеры/конфигурации json (первый, похоже, придерживается надежного анализа JSON, в то время как последний заканчивается в JSONPath?)
Комментарии:
1. В конечном счете я понял, что
Page<BookDetailsView>
прямое разоблачение было плохой идеей по другим причинам, и, изменив его на что-то другое, вся проблема исчезла. Однако я оставлю этот вопрос открытым на случай, если он имеет отношение к кому-то другому.
Ответ №1:
Я не знаю котлина, но вот в чем дело:
path()
не знает элемент в вашем списке, поэтому он будетLinkedHashMap
по умолчанию вместоBookDetailsView.class
- чтобы преодолеть это, вы можете предоставить для этого ссылку на тип.
пример java
List<BookDetailsView> response = ....then()
.extract().jsonPath()
.getObject("content", new TypeRef<List<BookDetailsView>>() {});
пример котлина
@Test
fun `extract nested`() {
var response = RestAssured.given().get("http://localhost:8000/req1")
.then()
.extract()
.jsonPath()
.getObject("content", object : TypeRef<List<BookDetailsView?>?>() {});
println(response)
//[{id=1}, {id=2}]
}
Комментарии:
1. Во многих библиотеках/оболочках Kotlin информация о типе
path<List<BookDetailsView>>("content")
будет овеществлена и использована для определения типа, но, похоже, в этой цепочке вызовов этого не происходит. Я думаю, что эквивалентом Kotlin для вашей java былbody().jsonPath().getObject("content[0]", object : TypeRef<BookDetailsView>() {})
бы, но теперь я получаюjava.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType
(я получаю одну и ту же общую ошибку, независимо от того, пытаюсь ли я захватить всю коллекцию или только определенный элемент в ней).2. @AwesomeTown Я протестировал с котином, и это сработало как заклинание.