Как Kotlin может помочь в тестировании API: кейс Русфинанс Банка +5




Заявленный в заголовке Kotlin больше ассоциируется с Android-разработкой, но почему бы не поэкспериментировать? Мы с его помощью нашли способ немного упростить автоматизацию тестирования API одного из наших сервисов, а также облегчить работу тестировщикам, мало знакомым с программированием и нюансами языка Java.

Чем мы занимаемся? Разрабатываем сервис для отправки брокерских анкет для расчёта и получения решения по ним. И несмотря на то, что это банковское решение, разработку ведёт небольшая scrum-команда, тестированием в которой занимаются 1-2 специалиста в зависимости от загрузки и ситуации на проекте.

Под катом расскажем о результатах наших экспериментов, которые мы с удовольствием перенесли в продакшн.

Без лица


Наш сервис разрабатывался для интеграции с интерфейсом партнеров и не имеет собственного UI. Поэтому в данном случае тестирование API напрямую — это единственный доступный вариант. Хотя и при наличии интерфейса API-тесты обладают рядом преимуществ:

  • позволяют начать тестирование уже на этапе разработки сервисов,
  • упрощают локализацию ошибок,
  • в целом уменьшают время на тестирование.

Наш сервис включает в себя набор API для передачи данных анкеты клиента на расчёт и для получения решения по автокредиту. В качестве основных высокоуровневых задач по тестированию можно выделить следующие проверки:

  • формат полей и параметров,
  • корректность кодов состояния и сообщений об ошибках,
  • обязательные поля и логика сервисов.



Переломный момент


MVP сервиса команда подготовила в очень сжатые сроки, а автоматизированные тесты изначально были написаны разработчиком продукта. Автотесты было быстрее и удобнее включить в проект, поэтому изначально они были тесно связаны его с кодом и базировались на стеке Java, REST-assured, с тестовым фреймворком TestNG.

С развитием проекта росла вовлеченность тестировщиков в процесс автоматизации. На данный момент задачи поддержки и обновления автотестов лежат полностью на отделе тестирования. В связи с этим появилась необходимость скорректировать подход к автоматизации — максимально упростить тесты и сделать их независимыми от основного кода. И, тем самым, решить возникающие трудности:

  1. Связь с кодом делает тесты более уязвимыми к любым проектным изменениям, требует больших трудозатрат на поддержку работоспособности.
  2. Сложные взаимосвязи увеличивают время на поиск информации и расследование ошибок.
  3. К работе с Java кодом сложно подключать новых сотрудников отдела тестирования, требуется обучение тестировщиков и значительное время для ввода в проект.

Поэтому мы решили изменить подход к тестированию и сформулировали следующие требования к новым инструментам:

  1. Полная совместимость с Java, позволяющая переписывать проверки поэтапно, при этом продолжая работать со старыми тестами.
  2. Простой, интуитивно понятный код.
  3. Бесплатность и стабильность языка.
  4. Выделение автотестов в отдельный проект.

Причём тут Kotlin


Нашим потребностям максимально отвечает язык Kotlin.



Кроме полной совместимости с Java в Kotlin нас заинтересовали следующие особенности:

  1. Лаконичность языка делает код наглядный и легко читаемым.
  2. Синтаксический сахар.
  3. Интуитивно понятный код и легкое вхождение специалистов в проект.
  4. Возможность переводить автотесты с Java на Kotlin постепенно и безболезненно, при необходимости частично.

Минусы, безусловно, тоже имеются, особенно в сравнении с Java. Во-первых, относительно небольшое сообщество: найти помощь и ответы на вопросы по Java гораздо проще, чем в случае с Kotlin. Во-вторых, в целом Java — более универсальный язык с большими возможностями и большим количеством наработок и библиотек.

Но, честно говоря, для целей тестирования нам не нужны настолько широкие возможности, которые может дать лишь Java. Поэтому мы отдали предпочтение простоте и лаконичности Kotlin.

От слов — к делу


В результате частичного перехода на новый язык мы получили заметный выигрыш по трудозатратам специалистов, значительное упрощение кода и сокращение его объема. Это хорошо заметно на конкретных примерах из тестов нашего сервиса для обработки брокерских анкет.

В Kotlin можно инициализировать объект запроса с помощью одной строки кода. Например, класс ApplicationDTO (для отправки анкеты на решение) и ErrorDTO (ошибки, приходящие от сервиса) выглядят так:



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, которую переиспользуем во всех тестах этого класса. Jacson серриализует и десерриализует json объекты, что упрощает дело: с ними не нужно работать как со строками, из-за этого существенно снижается количество ошибок.



С Kotlin мы существенно сокращаем количество кода, а значит, облегчаем жизнь и «писателям», и его «читателям». Класс ApplicationDTO уже содержит конструктор и некоторые другие методы (hashCode(), copy() и др.) — нам не нужно «перегружать» ими код и начинающие разработчики автотестов не отвлекаются на них. Их не нужно актуализировать при каких-то изменениях, что сокращает время внесения правок в тесты.

Также очень удобно, что Kotlin поддерживает именованные аргументы – код читается довольно легко, и нет надобности самим прописывать сеттеры для классов json объектов.

Пробовать новое — бесплатно


Хотелось бы отметить открытость и бесплатность Kotlin и всех используемых нами библиотек, что заметно упрощает работу и расширяет возможности для экспериментов и внедрения новых подходов. Согласитесь, бесплатно всегда проще пробовать новое, и в случае неудачи возвращаться к старым наработкам.

В будущем мы планируем развивать новый подход и масштабировать его на другие проекты, переводить остальные тесты API и пользовательского интерфейса на Kotlin.

P.S.: Несмотря на то, что Kotlin ассоциируется в первую очередь с Android- разработкой, сфера его использования, как вы поняли, этим вовсе не ограничивается. Сокращение времени на написание кода, его простота и лаконичность помогут снизить трудозатраты при решении многих задач. Если у нас появятся новые кейсы с Kotlin, мы обязательно расскажем о них тут.




К сожалению, не доступен сервер mySQL