Kotlin и API тесты
Маринский Рома Test Automation Engineer • IT-тусовщик • Творю добро • Езжу на электровелике и самокате
В плане • Как было • Почему Kotlin • Вооружаемся инструментами • Бросаем в бой Kotlin с API тестами • Выводы
https://github.com/rmarinsky/kotlinRestAssured
Как было Java 1.8 Gradle Rest-Assured jsonschema2pojo.org && RoboPOJOGenerator Jackson2
JPet testPet = new JPet(null, "Pet_" + this.randomString, this.randomNumber, null, null, Status.AVAILABLE); JPet petResponse = new JPetController().addNewPet(testPet); Assert.assertEquals(testPet, petResponse); Добавляем питомца в магазин Java
JPet testPet = new JPet(***); JPetController petCtrl = new JPetController(); petCtrl.addNewPet(testPet); petCtrl.deletePet(testPet); JMessage messageResponse = petCtrl.getPet(testPet); Assert.assertTrue(messageResponse.getMessage() .equals("Pet not found")); Удаление питомца из магазинa Java
И дальше было Java 1.8 Gradle Rest-Assured jsonschema2pojo.org && RoboPOJOGenerator Jackson2 assertjGen-gradle-plugin
JPet testPet = new JPet(***); JPet petResponse = new JPetController().addNewPet(testPet); JPetAssert.assertThat(petResponse).isEqualTo(testPet); Добавляем питомца в магазин Java Assertions generated
JPet testPet = new JPet(***); JPetController petCtrl = new JPetController(testPet); petCtrl.addNewPet(); petCtrl.deletePet(); JMessage messageResponse = petCtrl.getPet(); JMessageAssert.assertThat(messageResponse) .hasMessage("Pet not found"); Удаление питомца из магазинa Java Assertions generated
Не нужно писать код…
Java Pet Model
Java Pet Asserts
Lombok – скажите вы мне НЕТ – отвечу я вам
Kotlin от идеи до 1.0 – 6 лет 2010 2016
Меньше кода data class Customer(val name: String, val email: String) fun main(args: Array<String>) { val oleg = Cutomer(name = “Oleg”, email = println(“Name: ${oleg.name}nEmail: ${oleg.email}”) } object State { val global = “available" } Singleton Именованные аргументы String templates
var a: String = “abc” a = null //compilation error var b: String? = “abc” b = null //ok val data = mapOf(1 to “one”) data?.let { // execute block if not null } Null Safety val files = File("Test").listFiles() println(files?.size ?: "empty") files?.size ?: throw IllegalStateException(“File is missing!")
Null Safety class Product { private val name : String init { the() name = “” } private fun the() { println(name.lenght) } } Product() //java.lang.NullPointerExeption
Null Safety bookingList .items[0] .additionalInfo .pricesList[0] .baseAmount .uah bookingList .items? .get(0)? .additionalInfo? .pricesList? .get(0)? .baseAmount .uah
Extension functions fun WebElement.setValue(value: String): WebElement { this.clear() this.sendKeys(value) return this } city.setValue(“Lviv”) Расширяемый тип Наименование метода Возвращаемый тип
Если вы сейчас подумали написать новый «фреймворк» - то укусите своё подсознание - Бо он уже есть: Kirk github.com/SergeyPirogov/kirk
Reified types inline fun <reified T> String.toKotlinObject(): T { val mapper = jacksonObjectMapper() return mapper.readValue(JsonObject(this).encode(), T::class.java) } fun parseHardModel(): Product { val resp = prepareResp() return resp.toKotlinObject() } Передаваемый тип Расширяемый тип Возвращаемый тип
Вооружаемся инструментами
HTTP клиенты Fuel Khttp Что включает в себя Fuel-http, fuel-android, fuel-livedata, fuel-rxJava, fuel-Gson, fuel-jackson http-client Написать тест GET, 5 params, check response На разобраться и написать 1 час. Требуется глубоко разобраться в синтаксисе и возможностях 10 минут Глобальные конфигурации Настраиваемый Не настраиваемый (можно написать) Асинхронные запросы Поддерживает Поддерживает Доступность Tutorials, хорошая документация, большой комьюнити Минимальная документация
Fuel.post("http://httpbin.org/post").response { request, response, result -> println(request) println(response) val (bytes, error) = result if (bytes != null) { println(bytes) } } "http://httpbin.org/get".httpGet().responseString { request, response, result -> when (result) { is Result.Failure -> { error = result.getAs() } is Result.Success -> { data = result.getAs() } } } Fuel
FuelManager.instance.basePath = “https://seleniumcamp.com” Fuel.get("/get").response { request, response, result -> } FuelManager.instance.baseHeaders = mapOf("Device" to "Android") Fuel.get("/get").response { request, response, result -> } Fuel
get("http://httpbin.org/ip").jsonObject.getString("origin") post("http://httpbin.org/post", data = mapOf("key" to "value")) get("https://my.api/some/endpoint", heads = mapOf("X-API-Key" to "secret")) Khttp
json конверторы JsonToKotlinClass RoboPOJOGenerator Звёздочки 205 378 Поддержка сериализации GSON, Jackson, FastJSON, MoShi LoganSquare, Custom GSON, Jackson, FastJSON, MoShi, Logan Square, AutoValue Язык Kotlin Java, Kotlin Настройки Базовая инициализация(null, type value, none) Коммент с примером значения val / var тип переменной Nullability Только для Java (гетеры, сеттеры)
- in development XML – только через консольку XJC с JAXB
Assert библиотеки Kluent ~640 различных методов для проверок: Object, Boolean, Numeric, CharSequence, String, Collections, Exception, Map, Iterable, Files, Time + custom Moking Expekt ~ проверок, Object, Boolean, Numeric, CharSequence, Collections, Exception, Map, Files AssertK ~100 различных провероверок для Object, Numeric, Boolean, CharSequence, Collections, Exception, Map, Files + custom
val alice = Person("Alice", "Bob") val jon = Person("Jon", "Doe") val list = listOf(alice, jon) list shouldContain jon "hello" `should not equal` "world" kluent
23.should.equal(23) "Kotlin".should.not.contain("Scala") listOf(1, 2, 3).should.have.size.above(1) Expekt
Дальше будет Kotlin код
RestAssured.given().baseUri(BASE_URI) .`when`() .delete(PET_ENDPOINT + id) .then() .extract() .`as`(Message::class.java)
RestAssured.given().baseUri(BASE_URI) .`when`() .delete(PET_ENDPOINT + id) .then() .extract() .`as`(Message::class.java)
fun RequestSpecification.When(): RequestSpecification { return this.`when`() } inline fun <reified T> Response.As(): T { return this.`as`(T::class.java) } Extension functions with reified type functions
RestAssured.given().baseUri(BASE_URI) .When() .delete(PET_ENDPOINT + id) .then() .extract() .As() RestAssured.given().baseUri(BASE_URI) .`when`() .delete(PET_ENDPOINT + id) .then() .extract() .`as`(Message::class.java) Было Стало
Kotlin, жги Kotlin Gradle rest-assured JsonToKotlinClass jackson-kotlin
JsonToKotlinClass Plugin
JsonToKotlinClass Plugin Settings
Kotlin Pet model data class Pet( var photoUrls: List<String> = listOf(), var category: Category = Category() var name: String = “”, var id: Long = “”, var tags: List<Tag> = listOf(), var status: String = “” )
@Test fun `Add new pet to store test`() { val testPet = KPet(id = randomNumber, name = "Pet_${randomString}", status = Status.AVAILABLE) val petResponse = PetController(testPet).addNewPet() Assert.assertEquals(testPet, petResponse) } Kotlin
@Test fun `Delete pet from store test`() { val testPet = KPet(id = randomNumber, name = "Pet_${randomString}", status = Status.AVAILABLE) PetController(testPet).apply { addNewPet() deletePet() Assert.assertTrue(getPet() .message.equals("Pet not found")) } } Kotlin
Kotlin, жги Kotlin Gradle rest-Assured JsonToKotlinClass jackson-kotlin Kluent
val testPet = KPet(***) val petResponse = KPetController(testPet).addNewPet() petResponse shouldEqual testPet Добавление питомца в магазин Kotlin проверяем с kluent
val testPet = KPet(***) KPetController(testPet).addNewPet() shouldEqual testPet Добавление питомца в магазин Kotlin проверяем с kluent
val testPet = KPet(***) KPetController(testPet).apply { addNewPet() deletePet() getPet().message shouldEqual "Pet not found" } Удаление питомца из магазина Kotlin проверяем с kluent
Финалочка • Быстрее пишешь код: • Меньше кода -> быстрее понимаешь, что он делает • Легкая расширяемость кода (extensions/reified) • Осваивается за 2-4 недели • Прагматичное саморазвитие -> фан фактор командный • Привлекательность проекта • Медленную сборку проекта + 2x - 6x • org.gradle.caching=true (gradle.properties) – 4x – 10x
https://github.com/rmarinsky/kotlinRestAssured
Thanks and Questions? github.com/rmarinsky linkedin.com/rmarinsky medium.com/@newromka facebook.com/newromka t.me/newromka

Kotlin with API tests

Editor's Notes

  • #2 Представиться нужно
  • #4 Доклад о том как котлин решает проблемы лучших практик функциональной автоматизации тестирования с Java. И в конечном итоге вы узнаете какие фичи котлин, инструменты и библиотеки упростят написание автоматизированных функциональных API тестов. Но при этом какие недостатки в этом подходе существуют
  • #5 Сделать QR код для списка ссылок, сделать в описании репозитория все ссылки.
  • #6 http://petstore.swagger.io
  • #8 Здесь хотелось бы убрать null и не писать new
  • #9 Ну а тут не хочется писать часто petAction
  • #12 Ну а тут не хочется писать часто petAction
  • #19 Что такое котлин - это не просто обёртка над джавой, это уже мальтипрагматичный язык со всеми вытекающими. Это значит что вы можете писать код как вам угодно для реализации решения задачи. Т.е. вы можете написать одну независимую функцию, которая не определена в объекте, а просто выполняет какую-то операцию где угодно. И при этом вы можете писать на чистом ООП, а потом взять и всунуть обработку асинхронных функций в корутины - на зависть всем остальным языкам. Поэтому Котлин не просто обёрточка над джавой, а мультипрагматичный язык, у которого недавно было пополнение в виде Kotlin Native.
  • #20 Для компании JetBrains это стоило 6 лет времени чтобы выкатить версию 1.0, это вам не жс выплюнутый в мир за 10 дней в котором каждая строка чёрт знает когда выполняется :)
  • #22 Рассказать про то что нет тернарного оператора
  • #29 Рассказать про то что теперь можно с новой мощью написать свою обёртку над селениумом
  • #43 Это 2 моих метода помошника, которые расширяют возможности классов. Где первый просто расширяет, а второй позволяет указывать передаваемый тип не как аргумент метода/функции, а как тип для дженерика. При этом смотрите что выходит далее
  • #55 Меньше кода, реально меньше кода особенно благодаря дата классам, по сравнению с поджо, гораздо быстрее можно понять что может этот класс Очевидно что следует изучить котлин в первую очередь. Кривая обучения не будет прямой, главное не экспонента) Да в начале для решения задач потребуется в два раза больше времени, но от этого грустнее не становится, наоборот увлечение от конструкций кода всё компенсирует. Все будут рады применить какую-то новую штуку при том что можно просто сконвертировать текущий проект. Более того можно привлекать матёрых кандитатов, что для android разработки, что для автоматизации на данный момент технологического хайпа. А для андроид так вообще фрп котлин наверно лучший среди всех остальных языков Быстрая работа это не про кол-во написанных фич, а про написание, чтение, рефакторинг, тестирование кода и пр.. В среднем чтобы изучить более менее котлин потребуется 2-4 недели и ещё недели 2 чтобы писать быстрее чем на Java. Есть к сожалению ещё проблемы с автодополниением, поскольку он выполняется пока что только через плагин (на маке быстрее работает чем на винде, но навинде норм будет работать если поставить высокий приоритет процессу идеи). Ну или ждать идею 2018 или третью андроид студию По поводу чтения кода, тут не всё так гладко, да кода меньше, но пока, что готового стайл гайда и единого от гугл или jetbrain нет, даже по докам не всегда однородные примеры можно найти. Но в целом комьюнити и разработчики языка уже утвердили 99% стайл гайда и готовят доки на это. Про тестирование кода и расширению функционала, чего только стоят екстеншн функции и reified типы Саморазвитие, практически полностью новый язык это не только кучу новых ключевых слов, это новые способы переписать давно привычные паттерны, новые способы выражать свои мысли в коде новые способы думать. Вытолкните себя из зоны комфорта, ради этого фан фактора, стоящего фан фактора. Ну а это уже касается не только котлина, но и любой другой технологии, так что жгите и развивайтесь!
  • #56 Сделать QR код для списка ссылок, сделать в описании репозитория все ссылки.