![](https://habrastorage.org/webt/zi/ox/ty/zioxtyjds9tknxeh2-mdfvgkz90.png)
Заявленный в заголовке Kotlin больше ассоциируется с Android-разработкой, но почему бы не поэкспериментировать? Мы с его помощью нашли способ немного упростить автоматизацию тестирования API одного из наших сервисов, а также облегчить работу тестировщикам, мало знакомым с программированием и нюансами языка Java.
Чем мы занимаемся? Разрабатываем сервис для отправки брокерских анкет для расчёта и получения решения по ним. И несмотря на то, что это банковское решение, разработку ведёт небольшая scrum-команда, тестированием в которой занимаются 1-2 специалиста в зависимости от загрузки и ситуации на проекте.
Под катом расскажем о результатах наших экспериментов, которые мы с удовольствием перенесли в продакшн.
Без лица
Наш сервис разрабатывался для интеграции с интерфейсом партнеров и не имеет собственного UI. Поэтому в данном случае тестирование API напрямую — это единственный доступный вариант. Хотя и при наличии интерфейса API-тесты обладают рядом преимуществ:
- позволяют начать тестирование уже на этапе разработки сервисов;
- упрощают локализацию ошибок;
- в целом уменьшают время на тестирование.
Наш сервис включает в себя набор API для передачи данных анкеты клиента на расчёт и для получения решения по автокредиту. В качестве основных высокоуровневых задач по тестированию можно выделить следующие проверки:
- формат полей и параметров;
- корректность кодов состояния и сообщений об ошибках;
- обязательные поля и логика сервисов.
![](https://habrastorage.org/webt/7f/ja/so/7fjaso-t_pcza7_75cjddwzlt00.png)
Переломный момент
MVP сервиса команда подготовила в очень сжатые сроки, а автоматизированные тесты изначально были написаны разработчиком продукта. Автотесты было быстрее и удобнее включить в проект, поэтому изначально они были тесно связаны с его кодом и базировались на стеке Java, REST-assured, с тестовым фреймворком TestNG.
С развитием проекта росла вовлеченность тестировщиков в процесс автоматизации. На данный момент задачи поддержки и обновления автотестов лежат полностью на отделе тестирования. В связи с этим появилась необходимость скорректировать подход к автоматизации — максимально упростить тесты и сделать их независимыми от основного кода. И, тем самым, решить возникающие трудности:
- связь с кодом делает тесты более уязвимыми к любым проектным изменениям, требует больших трудозатрат на поддержку работоспособности;
- сложные взаимосвязи увеличивают время на поиск информации и расследование ошибок;
- к работе с Java кодом сложно подключать новых сотрудников отдела тестирования, требуется обучение тестировщиков и значительное время для ввода в проект.
Поэтому мы решили изменить подход к тестированию и сформулировали следующие требования к новым инструментам:
- полная совместимость с Java, позволяющая переписывать проверки поэтапно, при этом продолжая работать со старыми тестами;
- простой, интуитивно понятный код;
- бесплатность и стабильность языка;
- выделение автотестов в отдельный проект.
При чём тут Kotlin
Нашим потребностям максимально отвечает язык Kotlin.
![](https://habrastorage.org/webt/tf/1x/ru/tf1xrukehiyxt6pj5fsnmcc914w.jpeg)
Кроме полной совместимости с Java в Kotlin нас заинтересовали следующие особенности:
- лаконичность языка делает код наглядным и легко читаемым;
- синтаксический сахар;
- интуитивно понятный код и лёгкое вхождение специалистов в проект;
- возможность переводить автотесты с Java на Kotlin постепенно и безболезненно, при необходимости частично.
Минусы, безусловно, тоже имеются, особенно в сравнении с Java. Во-первых, относительно небольшое сообщество: найти помощь и ответы на вопросы по Java гораздо проще, чем в случае с Kotlin. Во-вторых, в целом Java — более универсальный язык с большими возможностями и большим количеством наработок и библиотек.
Но, честно говоря, для целей тестирования нам не нужны настолько широкие возможности, которые может дать лишь Java. Поэтому мы отдали предпочтение простоте и лаконичности Kotlin.
От слов — к делу
В результате частичного перехода на новый язык мы получили заметный выигрыш по трудозатратам специалистов, значительное упрощение кода и сокращение его объёма. Это хорошо заметно на конкретных примерах из тестов нашего сервиса для обработки брокерских анкет.
В Kotlin можно инициализировать объект запроса с помощью одной строки кода. Например, класс ApplicationDTO (для отправки анкеты на решение) и ErrorDTO (ошибки, приходящие от сервиса) выглядят так:
![](https://habrastorage.org/webt/h1/9r/rs/h19rrseo_sflcsmcqctj05dlqta.png)
Kotlin позволяет устанавливать значения полей при инициализации. Это очень экономит время написания тестов: при создании объекта поля уже заполнены валидными данными, мы изменяем только значения, которые относятся к текущей проверке.
Аннотация JsonIgnoreProperties позволяет не указывать все поля в классе, если для тестов они не нужны и их не нужно проверять. Если придёт какое-то поле, не указанное в описании, тест не упадёт с ошибкой.
Вопросительный знак у типа переменной показывает, что в какой-то момент она может принимать значение null. Но при использовании переменной это нужно будет не забыть учесть и обработать. Наконец, в отличие от тестов на Java, здесь можно изящно избежать NullPointerException.
Проиллюстрируем на простом примере проверки ошибки при некорректном логине:
class ApplicationTests {
val DEFAULT_LOGIN = "consLogin"
val ERROR_CODE = "3"
val BASE_URI = "https://base_url.com"
@Test
fun incorrectLogin() {
val incorrectLogin = DEFAULT_LOGIN + "INCORRECT";
val res = given()
.spec(spec)
.body(ApplicationDTO)
.`when`()
.post(endpointApplication(incorrectLogin)
.then()
.statusCode(400)
.extract().`as`(ErrorDTO::class.java)
assertThat(res.error_message).containsIgnoringCase("Указан неверный логин или пароль")
assertThat(res.error_code).isEqualToIgnoringCase(ERROR_CODE)
}
companion object{
private var spec: RequestSpecification? = null
@JvmStatic
@BeforeClass
fun initSpec() {
spec = RequestSpecBuilder()
.setContentType(ContentType.JSON)
.setBaseUri(BASE_URI)
.addFilter(ResponseLoggingFilter())
.addFilter(RequestLoggingFilter())
.build()
}
}
}
Мы создаем спецификацию для запросов в BeforeClass, которую переиспользуем во всех тестах этого класса. Jackson сериализует и десериализует json объекты, что упрощает дело: с ними не нужно работать как со строками, из-за этого существенно снижается количество ошибок.
![](https://habrastorage.org/webt/fq/pa/k5/fqpak5db9qr13ijac1q9bxgkpm8.png)
С Kotlin мы существенно сокращаем количество кода, а значит, облегчаем жизнь и «писателям», и его «читателям». Класс ApplicationDTO уже содержит конструктор и некоторые другие методы (hashCode(), copy() и др.) — нам не нужно «перегружать» ими код и начинающие разработчики автотестов не отвлекаются на них. Их не нужно актуализировать при каких-то изменениях, что сокращает время внесения правок в тесты.
Также очень удобно, что Kotlin поддерживает именованные аргументы — код читается довольно легко, и нет надобности самим прописывать сеттеры для классов json объектов.
Пробовать новое — бесплатно
Хотелось бы отметить открытость и бесплатность Kotlin и всех используемых нами библиотек, что заметно упрощает работу и расширяет возможности для экспериментов и внедрения новых подходов. Согласитесь, бесплатно всегда проще пробовать новое, и в случае неудачи возвращаться к старым наработкам.
В будущем мы планируем развивать новый подход и масштабировать его на другие проекты, переводить остальные тесты API и пользовательского интерфейса на Kotlin.
P.S.: Несмотря на то, что Kotlin ассоциируется в первую очередь с Android-разработкой, сфера его использования, как вы поняли, этим вовсе не ограничивается. Сокращение времени на написание кода, его простота и лаконичность помогут снизить трудозатраты при решении многих задач. Если у нас появятся новые кейсы с Kotlin, мы обязательно расскажем о них тут.