Как лучше мокать респонсы ретрофита?
Поделитесь, пожалуйста, как вы мокаете жсоны, когда нужно потестить работоспособность на конкретном жсоне?
Я раньше просто подкладывал .json файл вместо реального респонса в репозитории:
val mockJson: String = context.resources.openRawResource(R.raw.mock_json).bufferedReader().use { it.readText() }
val myObject = Gson().fromJson(mockJson, MyDTO::class.java)
Но тут проблема в том, что Gson не умеет String из мокового жсона конвертировать в энам.
Например, вот такой дто-класс не получится получить из жсона из-за содержащегося в нем энама:
@Serializable
data class MyDTO(
val accountNumber: String,
val accountBalance: String,
val agreementType: AgreementType
)
@Serializable
enum class CardAgreementType(val value: String) {
@SerialName("credit")
Credit("credit"),
@SerialName("debit")
Debit("debit")
}
Есть ли другие простые, но более рабочие решения для моканья таких респонсов?
Ответы (1 шт):
Вариант 1.
Gson умеет из коробки делать мапинг данных.
Это делается, с помощью аннотации @SerializedName
, пример:
val jsonString = "{\"name\":\"Юрий\",\"gender\":\"MALE\"}"
val person = Gson().fromJson(jsonString, Person::class.java)
enum class Gender {
@SerializedName("MALE")
MALE,
@SerializedName("FEMALE")
FEMALE
}
data class Person(
val name: String,
val gender: Gender
)
Но будте осторожны! если в ответе придёт, не то что вы ожидаете, можно получить креш:
val jsonString = "{\"name\":\"Юрий\",\"gender\":\"\"}"
val person = Gson().fromJson(jsonString, Person::class.java)
// здесь будет креш, так как пришёл null
val genderName = person.gender.name
Поэтому следует использовать nullable тип:
data class Person(
val name: String,
val gender: Gender?
)
Вариант 2.
Как алтернативное решение, можно сделать в ручную преобразование из string
в enum
.
Это может выглядеть так:
val myDTO = MyDTO(
"1234",
"1234",
"credit"
)
val myDTOModel = myDTO.mapToModel()
private fun MyDTO.mapToModel(): MyDTOModel {
return MyDTOModel(
accountNumber = accountNumber,
accountBalance = accountBalance,
agreementType = CardAgreementType.getValue(agreementType)
)
}
data class MyDTO(
val accountNumber: String,
val accountBalance: String,
val agreementType: String
)
data class MyDTOModel(
val accountNumber: String,
val accountBalance: String,
val agreementType: CardAgreementType
)
enum class CardAgreementType(val value: String) {
CREDIT("credit"),
DEBIT("debit"),
UNKNOWN("unknown");
companion object {
/**
* Получить тип enum по строковому значению
*/
fun getValue(value: String?): CardAgreementType =
entries.find { it.value == value } ?: UNKNOWN
}
}
Т.е вы получает сырой ответ в MyDTO, где agreementType
имеет тип string. Потом вам нужно преобразовать модель MyDTO в MyDTOModel, где переменная agreementType
уже является enum.
С помощью расширения MyDTO.mapToModel(), как раз и происходит преобразование.