company_banner

Тестирование From Zero to Hero. Часть 1

    Всем привет! Меня зовут Сергей, я работаю в команде Тинькофф. Сегодня я хочу рассказать, как мы в Тинькофф приходили к классической пирамиде тестирования. 

    Рассказ будет в трех частях:

    • Трудности, с которыми нам пришлось столкнуться, и как мы их преодолевали.

    • Конкретные решения по некоторым распространенным кейсам при написании интеграционных тестов.

    • Подход к написанию E2E-тестов (тестов, покрывающих взаимодействие всех систем приложения, включая back-end и пользовательский интерфейс) с использованием паттерна PageObject, пришедшего к нам из мира веб-разработки.

    Вместо предисловия

    Было время, когда солнце светило ярче, орки были зеленее, а мобильный банк Тинькофф писали восемь человек. Тогда почти все фичи катились без какого-либо тестового покрытия. В лучшем случае основные компоненты бизнес-логики могли быть покрыты Unit-тестами.

    Те времена давно прошли, Тинькофф уже далеко не маленький стартап, и количество разработчиков на одной платформе перевалило за полсотни. Количество кода, который вливается ежедневно, — огромно. И сейчас уже не стоит вопрос о необходимости полноценного покрытия тестами.

    Этап принятия

    Ранее E2E-тесты писали только автоматизаторы, которых было несколько человек. Тестами покрывалась только малая часть основного функционала. Регресс-тестирование начало затягиваться, и выкатывать фичи в прод стало труднее с учетом роста команды. 

    Первый шаг, который мы сделали, — передали написание E2E-тестов разработчикам, причем на обязательной основе. Ни одна фича не может перейти в статус Ready если она не покрыта E2E-тестами. Поскольку критерии приемки задачи описывают QA-специалисты, то они и писали тест-кейсы для каждой фичи.

    Тест-кейсы

    Тест-кейсы — это шаги, по которым QA-специалист проходит во время тестирования фичи. Сюда входят всевозможные проверки пользовательского флоу, реакция приложения на разного рода corner-case-проверки наличия UI-элементов на экране и так далее.

    Пример тест кейса
    Пример тест кейса

    Тут мы столкнулись с несколькими проблемами:

    • Мы стали писать тест-кейсы на каждый чих. То есть у нас были проверки в E2E-тесте, что при нажатом чек-боксе отображается некоторый текст, а в выключенном состоянии текст — другой. QA хотели проверить все кейсы и максимально обезопасить себя на стадии приемки, поэтому E2E-тесты начали походить на Unit-тесты с той лишь разницей, что на прогон таких тестов тратилось значительно больше времени и ресурсов.

    • Появилось большое количество тестов с повторяющимся флоу. Например, есть фича, в которой пользователь может заказывать справки. Пользователь попадал на экран «Списка справок», выбирал справку, после чего открывался экран «Настроек справки» (выбор языка, даты и так далее). После мы попадали на экран «Превью», где мы могли посмотреть PDF-файл нашей справки. 

      Мы имели три кейса, каждый из которых проверял отдельно каждый экран и не проверял пользовательский флоу. 

    • В E2E-тестах мы проверяли кейсы, которые в принципе не должны проверяться на этом уровне пирамиды. Например, факт отправки запроса и его составляющих или роутинг между экранами, который, по сути, спрятан в одной сущности.

    По итогу мы получали пирамиду тестирования в виде песочных часов. Мы делали много Unit- и E2E-тестов и практически не писали интеграционных тестов. 

    Почему это не круто?

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

    Е2Е-тесты хорошо проверяют интеграцию между всеми компонентами приложения, но обходятся они достаточно дорого по следующим причинам: 

    • Тесты запускаются на эмуляторах. Сам тест проходит долго, потому как он имитирует реальные действия пользователя (одна только авторизация длится около 30 секунд на каждом запуске теста).

    • Прогон Е2Е-тестов занимает много времени. Чтобы запускать такие тесты на пул-реквестах, нужно иметь достаточно мощностей. На данном этапе мы не можем этого делать. Это значит, что обратную связь о том, что что-то сломалось, мы получаем значительно позже. Но для нас это одна из приоритетных целей в области тестирования!

    • Из-за того, что прогоны тестов проводятся на CI, иногда достаточно трудно понять, по какой именно причине сломался тест.

    • Е2Е-тесты имеют свойство падать без каких-либо изменений в коде (нестабильность интернета, нестабильность тестового фреймворка, проблемы на реальном устройстве и другие причины), именно поэтому их еще называют flaky tests.

    По итогу мы пришли к выводу, что нам не хватает более дешевой и быстрой проверки интеграции компонентов приложения. Того самого среднего уровня пирамиды — интеграционных тестов, которые можно было бы запускать на пул-реквестах и не ждать длительных прогонов. 

    Почему так?

    Казалось бы, все эти разговоры о пирамидах тестирования давно затерты и пройдены много лет назад. Так почему разработчики по-прежнему делают одно и то же?

    Для E2E-тестов понятно, что на тестовом окружении должно быть поднято все, а для интеграционного — только часть и не всегда понятно какая. Это приводило к тому, что всем было проще не разбираться и писать E2E тесты. Это негативно сказывалось на времени прогона тестов в целом.

    Еще одной причиной была архитектура приложения. Она была не готова для комфортного написания интеграционных тестов. Поэтому, когда вставал вопрос — написать ли интеграционный тест на проверку корректности отправленного запроса в сеть с помощью E2E-теста или же написать тест, который это проверит на JVM, — выбор почти всегда был в пользу E2E. 

    Что мы сделали

    Командный тренинг

    Первое, что мы сделали, — командный курс. Выделили несколько команд-добровольцев, которые готовы были первыми попробовать на себе новые подходы к написанию тестов. Внутри компании провели несколько лекций и воркшопов, где разработчики вместе с QA могли разобрать какие-то тестовые кейсы, обсудить, какие кейсы и как лучше покрывать. 

    Так мы смогли больше понять проблемы друг друга и настроиться на одну волну. Мы пришли к пониманию, какие кейсы покрывать стоит, а какие нет. Мы поняли, в каком направлении нам двигаться в написании тестов, чтобы это не превращалось в кошмар для обеих сторон. 

    Пользовательские истории

    У нас было много обсуждений с QA, пока мы не пришли к формату тест-кейса, который должен показывать какую-то пользовательскую историю. 

    Небольшой пример

    У нас есть фича, в которой пользователь заказывает себе справку по счету. Здесь имеется три экрана:

    • Экран списка справок.

    • Экран настроек справки (язык, период и так далее).

    • Экран превью справки.

    Как бы это выглядело раньше?

    Мы бы проверили Экран списка справок: UI-элементы, отправляемые запросы. После чего аналогичные проверки были бы на другие два экрана. 

    Здесь было две проблемы:

    • Можно было покрыть тестами все, и при этом не было гарантии, что пользователь получит ожидаемый результат.

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

    Как это сделано сейчас?

    Тест-кейс != Е2Е-тест. 

    Есть тест-кейсы, в которых описана пользовательская история. К примеру, есть фича, в которой пользователь может заказать справку. Мы проверяем в тесте, что с точки входа внутрь фичи до конечного результата пользователь не встречает никаких проблем.

    Сам тест разделяется на шаги (steps). Обычно шаг — это один экран внутри всего флоу (но бывают и другие ситуации). Мы пишем в description название этого шага, делаем скриншот и все проверки, которые касаются этого шага. 

    По итогу если у нас что-то ломается — мы видим, на каком шаге был сломан тест, и понимаем, где находится проблема. Мы минимизируем количество запусков приложения на устройстве. Мы видим, что весь пользовательский флоу прошел успешно, а не только его отдельные компоненты. 

    И есть кейсы, в которых проверяется какое-то конкретное действие, например отправка запроса при нажатии на кнопку. Для таких тестов достаточно написать Unit-тест либо интеграционный тест. Такие ошибки мы можем отлавливать на стадии пул-реквестов и исправлять их до того, как они попадут к QA.

    Пирамида

    Архитектура приложения не была готова к тому, чтобы мы могли полноценно писать интеграционные тесты. Используемые подходы были хороши для команды из восьми человек, но стали сильно стрелять в ногу, когда количество людей в команде перевалило за полсотни. 

    При попытке написать какой-либо тест мы постоянно упирались в то, что где-то не хватает какой-то зависимости, а эта зависимость тянет еще несколько. Очень трудно было что-то замокировать: приходилось либо мокировать весь DI-модуль, либо пытаться мокировать большие и сложные запросы. Все это вызывало боль и полное нежелание писать такие тесты.

    Мы с ребятами начали разбивать монолитные компоненты на фича-модули. Наша задача — вынести компоненты приложения, которыми пользовались все фича-команды (сетевые слои, работа с базами данных, аналитика и другие). 

    После чего внутри фича-команд создали задачи на рефакторинг своих фича-модулей и бизнес-сущностей, многие из которых рефакторятся в процессе написания тестов. 

    Также мы смогли показать остальным разработчикам примеры, как стоит писать такие тесты. Было подготовлено базовое окружение для написания интеграционных тестов: 

    • Выделили отдельные Аndroid-модули для тестовых компонентов.

    • Подготовили базовые моки (сущности хранения сессий, базы данных и так далее).

    • Написали документацию по основным принципам тестирования с примерами.

    Итоги

    Мы пришли к осмысленной, правильной пирамиде тестирования.

    • Начали более сбалансированно распределять тесты по слоям.

    • Задали направление для всей команды, написали несколько тестов, которые можно брать за пример и не бояться потратить три дня на написание одного теста (да, поначалу было и такое).

    • Смогли задать направление для архитектурных изменений и упростить написание окружения вокруг тестов. 

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

    TINKOFF
    IT’s Tinkoff — просто о сложном

    Comments 6

      +1
      Если бы вы вернулись назад во времени, вы бы пошли тем же путем? Можно ли скипнуть этап «песочных часов» и пойти сразу к «пирамиде» или для развивающейся команды важно пройти этот путь, чтобы понимать, что и зачем существует?
        0
        Я думаю, что можно. Собственно для этого я делюсь здесь нашим опытом.

        Наверное если с самого начала на это закладываться, то в целом это не должно вызвать многих проблем.

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

        Мобильный банк Тинькофф — это первый и основной проект. Если взять другие проекты Тинькофф, более молодые, то там с этим дела обстоят лучше, т.к. у ребят на старте был опыт прошлых проектов и ресурсы.
          0
          И если ответить конкретно на ваш вопрос. То думаю, что как первый проект — нет. Скорее всего это все равно пришлось бы пройти.
        +1

        Интересно будет прочесть опыт коллег о непосредственной реализации прогона E2E и интеграционных тестов в более конкретных технических терминах. Для нашего проекта (тоже банк) E2E UI тесты самые "медленные" и хрупкие, ибо то одно, то другое в плане интеграции "отваливается" в предпродакшн, и многое делается вручную, хотя очень просится автоматизироваться…

          0
          Работаю над этим=)
          –3

          "Всем привет! Меня зовут Сергей, я работаю в команде Тинькофф. Сегодня я хочу рассказать, как мы в Тинькофф приходили к классической пирамиде тестирования."… велосипедистов? А блин. Этож Хабр. Ухожу.

          Only users with full accounts can post comments. Log in, please.