Извлечение вложенного объекта POJO с уверенностью

#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 не знает, что делать, но я не совсем понимаю:

  1. Почему «простой» случай разбирается без проблем?
  2. Разве параметр типа, передаваемый path() функции, не должен указывать ей, какой тип использовать?
  3. Что нужно настроить, чтобы второй случай работал, ИЛИ
  4. Есть ли какой-то другой подход для захвата вложенного объекта, который имел бы больше смысла?

Покопавшись немного в коде, кажется, что в двух случаях на самом деле могут использоваться разные парсеры/конфигурации 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 Я протестировал с котином, и это сработало как заклинание.