У меня проблема с тестом (макет) типа POST в kotlin, когда я использую класс данных с полем date (LocalDate).
Это стек, который я использую:
springBoot : v2.1.7.RELEASE
Java : jdk-11.0.4
kotlinVersion : '1.3.70'
junitVersion : '5.6.0'
junit4Version : '4.13'
mockitoVersion : '3.2.4'
springmockk : '1.1.3'
Когда я выполняю метод POST в приложении, все в порядке, у меня есть ответ, и данные правильно сохраняются в БД:
curl -X POST "http://127.0.1.1:8080/v1/person/create" -H "accept: */*" -H "Content-Type: application/json" -d "[ { \"available\": true, \"endDate\": \"2090-01-02\", \"hireDate\": \"2020-01-01\", \"id\": 0, \"lastName\": \"stringTest\", \"name\": \"stringTest\", \"nickName\": \"stringTest\" }]"
Но когда я пытаюсь выполнить тест метода POST, я не могу (только с методом POST, с GET все в порядке)
Это классы, которые я использую:
Файл Person.kt
@Entity
data class Person(
@Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.AUTO)
var id: Long,
var name: String,
var lastName: String,
var nickName: String,
@JsonFormat(pattern = "yyyy-MM-dd")
var hireDate: LocalDate,
@JsonFormat(pattern = "yyyy-MM-dd")
var endDate: LocalDate,
var available: Boolean
) {
constructor() : this(0L, "Name example",
"LastName example",
"Nick example",
LocalDate.of(2020,1,1),
LocalDate.of(2090,1,1),
true)
Файл PersonService.kt
@Service
class PersonService(private val personRepository: PersonRepository) {
fun findAll(): List<Person> {
return personRepository.findAll()
}
fun saveAll(personList: List<Person>): MutableList<person>? {
return personRepository.saveAll(personList)
}
}
Файл PersonApi.kt
@RestController
@RequestMapping("/v1/person/")
class PersonApi(private val personRepository: PersonRepository) {
@Autowired
private var personService = PersonService(personRepository)
@PostMapping("create")
fun createPerson(@Valid
@RequestBody person: List<Person>): ResponseEntity<MutableList<Person>?> {
print("person: $person") //this is only for debug purpose only
return ResponseEntity(personService.saveAll(person), HttpStatus.CREATED)
}
}
И, наконец,
PersonApiShould.kt (этот класс является проблемой)
@EnableAutoConfiguration
@AutoConfigureMockMvc
@ExtendWith(MockKExtension::class)
internal class PersonApiShould {
private lateinit var gsonBuilder: GsonBuilder
private lateinit var gson: Gson
lateinit var mockMvc: MockMvc
@MockkBean
lateinit var personService: PersonService
@BeforeEach
fun setUp() {
val repository = mockk<PersonRepository>()
personService = PersonService(repository)
mockMvc = standaloneSetup(PersonApi(repository)).build()
gson = GsonBuilder()
.registerTypeAdapter(Person::class.java, PersonDeserializer())
.create()
gsonBuilder = GsonBuilder()
}
@AfterEach
fun clear() {
clearAllMocks()}
@Test
fun `create person`() {
val newPerson = Person(1L,
"string", //name
"string", //lastName
"string", //nickname
LocalDate.of(2020, 1, 1), //hireDate
LocalDate.of(2090, 1, 2), //endDate
true) //available
val contentList = mutableListOf<Person>()
contentList.add(newPerson)
// also tried with
// every { personService.findAll() }.returns(listOf<Person>())
// every { personService.saveAll(mutableListOf<Person>())}.returns(Person())
every { personService.findAll() }.returns(contentList)
every { personService.saveAll(any()) }.returns(contentList)
/* didn't work either
val personJson = gsonBuilder.registerTypeAdapter(Date::class.java, DateDeserializer())
.create().toJson(newPerson)
*/
val content = "[\n" +
" {\n" +
" \"available\": true,\n" +
" \"endDate\": \"2090-01-02\",\n" +
" \"hireDate\": \"2020-01-01\",\n" +
" \"id\": 0,\n" +
" \"lastName\": \"string\",\n" +
" \"name\": \"string\",\n" +
" \"nickName\": \"string\"\n" +
" }\n" +
"]"
val httpResponse = mockMvc.perform(post("/v1/resto/person/create")
.content(content) //also tried with .content(contentList)
.contentType(MediaType.APPLICATION_JSON))
.andReturn()
// error, because, httpResponse is always empty
val personCreated: List<Person> = gson.fromJson(httpResponse.response.contentAsString,
object : TypeToken<List<Person>>() {}.type)
assertEquals(newPerson.name, personCreated.get(0).name)
}
У Gson есть некоторые проблемы при десериализации дат, это парсер (хак), он работает для моего метода GET
File PersonDeserializer.kt
class PersonDeserializer : JsonDeserializer<Person> {
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Person {
json as JsonObject
val name = json.get("name").asString
val lastName = json.get("lastName").asString
val nickName = json.get("nickName").asString
val available = json.get("available").asBoolean
val hireDate = LocalDate.of((json.get("hireDate") as JsonArray).get(0).asInt,
(json.get("hireDate") as JsonArray).get(1).asInt,
(json.get("hireDate") as JsonArray).get(2).asInt)
val endDate = LocalDate.of((json.get("endDate") as JsonArray).get(0).asInt,
(json.get("endDate") as JsonArray).get(1).asInt,
(json.get("endDate") as JsonArray).get(2).asInt)
return Person(1L, name, lastName, nickName, hireDate, endDate, available)
}
}
Я вижу, что ошибка в MOCKK Library, потому что из теста я могу добраться до конечной точки и правильно напечатайте значение
печать с конечной точки: print ("person: $ person") // эта строка находится в конечной точке
Person: [Person(id=0, name=string, lastName=string, nickName=string, hireDate=2020-01-01, endDate=2090-01-02, available=true)]
Журнал ошибок тестирования
19: 27: 24,840 [main] DEBUG io.mockk.impl.recording.states.AnsweringState - Throwing io.mockk.MockKException: не найден ответ для: PersonRepository (# 1) .saveAll ([Person (id = 0, name = string, lastName = string, nickName = string, hireDate = 2020-01-01, endDate = 2090-01-02, available = true)]) в PersonRepository (# 1) .saveAll ([Person (id = 0, name = string, lastName = string, nickName = string, hireDate = 2020-01-01, endDate = 2090-01-02, доступно = true)])
19: 27: 24.844 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - не удалось полный запрос: io.mockk.MockKException: не найден ответ для: PersonRepository (# 1) .saveAll ([Person (id = 0, имя = строка, l astName = string, nickName = string, hireDate = 2020-01-01, endDate = 2090-01-02, доступно = true)])
org.springframework.web .util.NestedServletException: запрос обработка не удалась; вложенное исключение: io.mockk.MockKException: нет найден ответ для: PersonRepository (# 1) .saveAll ([Person (id = 0, name = string, lastName = string, nickName = строка, hireDate = 2020-01-01, endDate = 2090-01-02, доступно = true)])
Ошибки варьируются в зависимости от исправления, также я получил
JSON ошибка разбора: Невозможно десериализовать значение типа java.time.LocalDate
из ... ... еще 48
Но всегда та же проблема с сериализацией LocalDate весной с Kotlin
Любая помощь, которую вы можете предоставить, будет принята с благодарностью.