Без хайпи і маркетингу: чи потрібен вам Kotlin?

В рамках QAFest 2017 автор блогу automation-remarks.com і Lead QA Automation Engineer в Ciklum Сергій Пирогов розповів, як він докатилися до Kotlin і що з цього вийшло. Для читачів DOU, які не були присутні на конференції, Сергій виклав цей досвід в авторській статті.

Мене звуть Сергій Пирогов. Вже більше п'яти років я займаюся питаннями автоматизації тестування на проектах різного масштабу і складності. В основному я займаюся автоматизацією тестування з використанням технологій зі світу Java.

Kotlin на поточний момент однозначно знаходиться на піку популярності і у тестуванні може принести чимало користі. Але не все так просто, в чому ви можете переконатися з цієї статті.

Передісторія: Java, Groovy і інші альтернативи

Java є найстарішим і популярним мовою на JVM. Відмінний мова та інфраструктура. Безперечний фаворит різних рейтингів серед мов програмування. На Scala ми проектів писати не пробували з-за зайвої складності, а ось на Groovy писали і досить успішно. Цим досвідом я ділився на конференції Selenium Camp 2016 . На жаль, Groovy вмирає і поява Kotlin його агонію лише прискорює.

Звідки взявся цей ваш Kotlin?

Kotlin — досить молодий мову, який розробляється і спонсорується компанією JetBrains. З відкритих джерел можна дізнатися, що в розробку мови було вкладено понад $15 млн, а сам мова — це ще один спосіб популяризувати компанію і ще більше підвищити продажі для Idea.

Мова почав набирати популярність після того, як на конференції JavaOne 2015 Hans Dockter, CEO of Gradle, заявив, що Kotlin отримує офіційну підтримку для написання Gradle білд-скриптів. Тоді Kotlin все ще був в беті, але новина сколихнула всіх небайдужих. Хвиля хайпи почала підніматися вже в той момент. На піку популярності мова опинився в травні цього року на конференції Google I/O, де було оголошено про те, що Kotlin поряд з Java стає офіційною мовою розробки під платформу Android. Тут-то і «лупануло»: весь Twitter був в постах про Kotlin, з'явилася купа блог-постів з визнаннями в любові мови. Представники JetBrains в різних джерелах стали заявляти, що Kotlin — це майбутнє розробки на JVM.

В цілому, якщо дивитися на ситуацію тверезо, то причини хайпи цілком зрозумілі. Джава розвивається надто повільно. Java 8 з'явилася аж в 2014 році, Java 9 на момент публікації вже вийшла, але в самій мові дуже мало смачних фішок. Більш того, з Java 9 у багатьох все в момент перестало працювати. І тут людям дають мову, наповнений фичами, частина з яких з'явиться тільки в 10-ке (і то з застереженням «може бути ...»).

Чим цей ваш Kotlin крутіше?

Автори мови зізнаються, що не намагалися придумати щось кардинально нове. Мова спеціально замислювався максимально прагматичним і зручним у використанні для розробників. Ось невеликий список фішок, які є в Kotlin:

З більш повним списком можна ознайомитися за посиланням .

І що? Як воно допомагає жити?

Null safety — це selling point Kotlin. Перевірка на nullable type здійснюється ще під час компіляції.

var a: String = "abc"
a = null // compilation error

var b: String? = "abc"
b = null // ok

Це дуже зручно і допомагає уникнути багатьох помилок.

Extension functions — це фіча, якої мені особисто дуже не вистачає в Java. Нижче приклад, як за допомогою пари функцій можна поліпшити існуючий Selenium API.

fun WebDriver.open(url: String) {
get(url)
}

fun WebDriver.all(cssselector: String): MutableList<WebElement>? {
 return findElements(By.cssSelector(cssselector))
}

fun List<WebElement>.shouldHave(size: Int) {
 assert(this.size == size)
}

За підсумком ми можемо писати тести в такому форматі:

val driver = ChromeDriver()
driver.open("http://automation-remarks.com")
driver.all(".post").shouldHave(size = 9)

Ще однією фішкою є функції типу .apply, .with. З їх застосуванням можна зробити код більш компактним.

ChromeDriver().apply {
open("http://automation-remarks.com")
 all(".post").shouldHave(size = 9)
}

String template — дозволяє зручно форматування рядка. Ми дуже часто користуємося цим у тестах.

fun findCustomerById(id:Long): CustomerEntity{

 val query = """
 SELECT *
 FROM customer
 WHERE id = $id;
"""

 return findOne(CustomerEntity::klass, query)
}

Як бачите, SQL запит не містить потворних переносів рядків і конкатенаций. Його просто читати, копіювати та редагувати.

Reified type — фішка, яка дозволяє зробити ваш код дуже красивим і лаконічним. Наприклад, ми в тестах часто користуємося бібліотекою Apache DBUtils . З її застосуванням код виходить таким:

val beanHandler = BeanHandler<City>(City::class.java)
val city: City = QueryRunner().query("SELECT * FROM city", beanHandler)

Але, застосувавши вже знайомі нам extension function і reified type, ми можемо зробити так:

inline fun <reified T> QueryRunner.findOne(sql: String): T {
 return BeanHandler(T::class.java).run { query(sql, this) }
}

і отримати наступний код:

val city : City = QueryRunner().findOne("SELECT * FROM city")

У цьому випадку тип, в який конвертується результат запиту буде братися типу оголошеної змінної. Це набагато зручніше, ніж Java Узагальнення.

Сумісність з Java

З офіційної документації відомо, що Kotlin розроблявся з огляду на максимальну сумісність з Java. Java Interop подається під соусом, що ми можемо взяти будь-код, написаний раніше, і викликати в Kotlin. Або ж назад — няшный Kotlin-код викликати в сумній джавке.

Чи Так це насправді? Давайте розбиратися.

Kotlin vs Rest Assured

Для написання тестів для API ми використовуємо одну з найкращих бібліотек — Rest Assured . Подивимося, як вона буде працювати в Kotlin.

Опа-опа, when зарезервоване слово. Обійти таке обмеження можна, обернувши його в такі ось цікаві лапки. Скажу чесно, я навіть не відразу знайшов ці символи на своїй клавіатурі :-)

Kotlin + Selenide

Для написання UI тестів ми використовуємо Selenide. Давайте подивимося на сумісність.

І знову невдача — $ не можна, val теж не можна.

Kotlin + Hamcrest (AssertJ)

Всі ми при написанні тестів активно використовуємо такі бібліотеки, як Hamcrest і AssertJ. Що з сумісністю?

Тут нас теж чекають обмеження.

Все через милиці

Коли починаєш натикатися на такі обмеження, то напрошується цілком логічна думка...

Чиним Kotlin і Rest Assured

Насправді всі попередні приклади можна в якійсь мірі полагодити. Дивимося на приклад з Rest Assured:

fun RequestSpecification.When(): RequestSpecification {
 return this.`when`()
}

@Test
fun basicPingTest() {
given()
.When()
.get("/garage")
.then().statusCode(200);
}

Робимо extension function, який обертаємо виклик when. Працює? Працює. Так, це милицю, але робочий.

Чиним Kotlin vs Selenide

У випадку з Selenide нам потрібно просто обернути виклики функцій $ і $$, a замість .val() викликати .setValue().

get fun(selector: String) : SelenideElement {
 return `$`(selector);
}

 fun all(selector: String) : ElementsCollection {
 return `$$`(selector);
}

Результат:

@Test fun usingDollarsWithBackticks() {
get(By.name("q")).setValue("selenide")
 all("#ires .g").shouldHave(size(10))
 get("#ires .g").shouldHave(text("Kotlin"));
}

Чиним Kotlin + Hamcrest (AssertJ)

На жаль, за цим пунктом нас чекає розчарування. Якщо Hamcrest ще якось сумісний з Kotlin, то AssertJ полагодити не вийде через несумісність в Generic types. Тут нам потрібно просто взяти і замінити бібліотеку. Благо, в GitHub вже є ентузіасти, які написали порт — assertk .

@Test fun example(){
assertThat(1).isEqualTo(1)
}

assert {
 throw Exception("error")
}.throwsError {
it.hasMessage("wrong")
}
// -> expected [message] to be:<["wrong"]> 
 but was:<["error"]>

Слід зазначити, що assertk має більш зручним API і повністю сумісна з Kotlin.

Начебто всі наші проблеми ми «підлікували», ну або хоча б підставили милиці. Природно, ви не можете натрапити на проблеми, наведені вище, якщо на старті проекту будете вибирати бібліотеки і технології, сумісні з Kotlin.

Чим же все-таки хароший Kotlin?

В доповнення до мовних фічами і синтаксичним конструкціям, можу відзначити, що мова дуже лаконічний і дозволяє будувати зручні DSL. В підтвердження покажу приклад тесту, написаного з застосуванням бібліотеки Kirk , яка покликана замінити Selenide для Kotlin.

Приклад чітко демонструє, якого формату DSL можна писати. По суті — це BDD, тільки в коді, зі всіма плюшками у вигляді строгої типізації, автодополнениями і підтримкою рефакторінгу.

Ще одним прикладом є Gradle Kotlin-DSL . Вже зараз gradle.build файл можна писати більш приємним способом, отримуючи автодоповнення і статичну компіляцію. Ця фіча поки що не досягла стадії релізу, але я більше ніж упевнений: коли буде 1.0, Groovy DSL можна помахати ручкою та повністю перейти на Kotlin.

Що маємо у підсумку?

Kotlin — дуже приємний мову. Все, що вже реалізовано у конкурентів Java, в ньому є. Конвертувати існуючий код на Java в Kotlin трохи проблематично. Немає поки що повної сумісності з усіма найбільш популярними Java-фреймворками і бібліотеками. Особисто я отримую задоволення від роботи з мовою. На закінчення я не буду давати явних рад — писати чи не писати, вчити Kotlin або хейтить і йти вчити JavaScript. Я просто залишу вам ссилочку на свіже репорт від Rebel Labs про стан Java-екосистеми , в якому Kotlin названий самим улюбленим мовою c коефіцієнтом задоволеності 9.1 10.

Дякую, що читали. Завжди думайте головою, не ведіться на хайп і маркетинг. Вибирайте технології під свої завдання.

Опубліковано: 02/10/17 @ 07:00
Розділ Блоги

Рекомендуємо:

Ruby/Rails дайджест #10: подкаст з DHH, новий блог про розробку, виступи на RubyKaigi
React 16: огляд нової архітектури fiber
DOU Labs: як харків'яни створили IT-музей
Кібербезпека по-українськи: про тиск силовиків, білих і чорних хакерів і цінності диванних експертів
Junior дайджест: курси, стажування, інтернатура. Жовтень'17