Привет Хабр! Меня зовут Антон и я 4 года работаю QA . За это время я успел пройти путь от книжки Савина (куда же без неё) до организации процессов тестирования на небольших проектах.

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

Дано

На проекте есть Telegram бот с админкой. Соответственно действия которые произведены в админке отражаются для клиента в боте.

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

Определение требований

Нужен инструмент, который позволит:

  1. Тестировать бота в привязке к админке в вебе

  2. Работать с UI тестами

  3. Работать с API тестами

  4. (желательно) Не зависеть от выбора инструмента автоматизированного тестирования

Поиск готовых решений

По запросам похожим на “Автоматизация тестирования бота telegram” - несколько десятков статей про юнит-тесты, несколько видео про тестирование диалога с ботом и ничего по интересующей конкретно меня теме, возможно плохо искал, но я правда старался.

В какой-то момент даже нашёл относительно близкое к желаемому - фреймворк Botium, но, увы, у него не оказалось коннектора для Telegram. Так что я всё таки решился изобрести свой велосипед…

Идея

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

  1. В процессе изучения Telegram Bot API оказалось, что один бот не может писать другому боту и вообще “слышать” его.

  2. Все библиотеки реализующие ботов как правило асинхронные, а тесты у меня вполне себе синхронны.

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

Формирование решения

Итак, план, в целом, ясен, осталось только решить образовавшиеся проблемы.

Telegram API

С первой проблемой помог Хабр, а именно эта статья.

Оказалось у Telegram, помимо Telegram Bot API, есть ещё и Telegram API, который, как я понял, подразумевался как способ создания собственного Telegram клиента. Но так же это API даёт такую интересную возможность как создание, так называемых, юзер-ботов, которые работают под обычными пользователями, что позволяет такому боту взаимодействовать с обычным ботом написанным на Telegram Bot API.

Проблема асинхронности

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

В итоге мы имеем следующую схему:

  • Есть тесты, они просто записывают куда-то команды боту-тестировщику на отправку сообщения, либо читают сообщения, которые он прочитал и записал в очередь.

  • Сам бот-тестировщик работает абсолютно независимо от тестов. Просто ловит событие полученного сообщения от тестируемого бота и слушает очередь, в которую тесты записывают сообщения на отправку. Когда в очереди появляются записи, бот отправляет текст записи тестируемому боту.

Выбор инструментов

Реализовать свою затею решил на самом “ботовом” языке программирования (ну и на самом мне близком) - Python.

Есть несколько библиотек, которые реализуют взаимодействие через Telegram API, мне приглянулся Telethon.

Для реализации очереди сообщений использовал сервер Redis и одноимённую библиотеку Python.

Написание кода

Настройка сессии

Опустим описание процесса регистрации приложения на my.telegram.org и приступим к реализации самого бота. (подробнее о регистрации можно узнать тут)

from telethon import TelegramClient, sync

# Название сессии
session = 'tester_bot'
# Api ID и Api Hash полученные на my.telegram.org
api_id = 12345678
api_hash = '123456789qwerty987654321'

client = TelegramClient(session, api_id, api_hash)

async def main():
    # Выводим в консоль данные о текущем пользователе, для проверки
    me = await client.get_me()
    print(me.stringify())
    
    # Сюда в дальнейшем добавим вызов метода отправки сообщений
    
    # Бот будет запущен пока мы сами не завершим его работу
    await client.run_until_disconnected()

if __name__ == '__main__':
    with client:
        client.loop.run_until_complete(main())

После запуска скрипта в консоли надо ввести телефон от аккаунта Telegram, а затем код подтверждения. Потом в папке со скриптом должен создаться файл .session, который сохраняет вашу сессию и позволяет запускать бота без повторной авторизации через телефон + код.

Важно! Пока разбирался с библиотекой, Telegram счёл мои действия подозрительными, поэтому просто обрывал все сессии с моего IP. В дальнейшем я смог создать стабильную сессию, только с использованием VPN.

Очень рекомендую при получении кода в telegram заходить в официальный клиент с другого IP и устройства (например с телефона через мобильный интернет). И вообще стараться не дёргаться лишний раз до создания сессии.

Что ж, сессия настроена, но бот пока не может ни читать, ни отправлять сообщения

Redis

Для чтения и отправки сообщений из тестов потребуется Redis. Запускаем redis-server и добавляем вызов соединения с ним в скрипт.

from redis import Redis

redis = Redis(host=localhost, port=6379)

Чтение сообщений

Читаем сообщения и записываем их в очередь для полученных сообщений в Redis.

@client.on(events.NewMessage())
async def handle_new_message(event):
    try:
        if event.is_private:
            sender_username = await event.get_sender().username
            if sender_username == "username тестируемого бота":
                # Записываем полученное сообщение от тестируемого бота в очередь
                redis.rpush('response_messages', message.message)
    except Exception as e:
        print(f'Ошибка при чтении сообщения: {e}')

Отправка сообщений

А теперь читаем сообщения из очереди на отправку и отправляем их в Telegram.

async def handle_messages_to_send():
    while True:
        try:
            # Проверяем, что очередь не пустая
            if redis.llen('request_messages') != 0:
                # Забираем сообщение из очереди на отправку, декодируем и отправляем
                message = redis.rpop('request_messages').decode()
                await client.send_message("username тестируемого бота", message)
        except Exception as e:
            print(e)
            await asyncio.sleep(5)

Не забываем добавить вызов этой функции в main.

На этом базовая версия бота готова! Просто запускаем этот скрипт и забываем. Можно переходить к разработке самих тестов.

Тесты

На самих тестах зацикливаться не буду. Работаем с redis как будто это и есть бот. Покажу на примере pytest.

@pytest.fixture()
def tester_bot():
  redis = Redis(host='localhost', port=6379)
  yield redis
  # Чистим очереди после выполнения тестов
  redis.delete('response_messages')
  redis.delete('request_messages')

def test_response_message(tester_bot):
  # Выполяем какие-то действия через браузер или API
  ...
  # Получаем сообщение от бота
  message = tester_bot.rpop('response_messages').decode()
  assert message == 'Ожидаемое сообщение'

def test_request_message(tester_bot)
  # Закидываем сообщение в очередь на отправку
  tester_bot.rpush('request_messages', 'Привет')
  # Добавляем какое-нибудь ожидание
  time.sleep(2)
  # Получаем ответ от бота
  message = tester_bot.rpop('response_messages').decode()
  assert message == 'Привет, я бот! Получил твоё сообщение!'

По поводу ожиданий. Чтобы не ждать "глупо", можно реализовать ожидание, пока длина очереди полученных сообщений не изменится, хоть на том же WebDriverWait из Selenium.

Итог

Готово! Бот работает и отлично подключается к тестам, так же абсолютно не важно на каком языке вы пишите тесты, нужно только реализовать в тестах подключение к Redis. Конечно, для полноценной интеграции таких тестов в проект, необходимо дополнительно обернуть реализацию в удобный интерфейс, но целью статьи было показать саму концепцию таким же QA в поисках решения как и я!

Уверен, что всё что-то реализовано не очень хорошо. Если так, то буду только рад обсудить это в комментариях!

Комментарии (3)


  1. FTrukhacev
    00.00.0000 00:00

    Спасибо за публикацию. Был бы рад ещё почитать ваши статьи на тему автоматизации тестов. Я заканчиваю обучение на одном из известных онлайн-курсов. Там как раз есть тема про автоматизацию, но материала, конечно, маловато будет. Хотелось бы узнать тонкости по настройке разных проверок, которые учитывают нажатия кнопок, заполнение форм для веба и т.д.

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

    Ну и если возможно, буду рад подсказке о том, что можно поизучать на эту тему.


    1. dikobra4 Автор
      00.00.0000 00:00
      +1

      Спасибо за комментарий!) Если пойму, что есть необходимость, постараюсь написать несколько статей про старт автоматизации web. Но на данный момент, мне кажется, что информации в интернете по этой теме достаточно много)
      По подсказкам постараюсь помочь с направлением, не уверен насколько легитимно скидывать сюда ссылки, но думаю можно обойтись и без них:

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

      2. Надо базово изучить/осознать язык программирования, на котором вы будете писать и присмотреть инструменты, с помощью которых вы будете автоматизировать (классика на Python - pytest/selenium, если мы говорим о UI)

      3. По тому же самому Selenium очень много материалов, правда часть из них прилично устарела, так что не забудьте сверяться с официальной документацией (она сейчас очень хорошая и с примерами кода на разных ЯП)

      4. Если попали в тупик в процессе изучения - StackOverflow и ChatGPT в 95% случаев помогут вам разобраться.

      Главное не сдаваться из-за ошибок и не ждать быстрого результата! Более-менее нормальную автоматизацию для небольшого проекта я смог запустить только с третьего раза и всё это было очень не быстро. А сколько трудностей ещё впереди мне даже страшно думать)


      1. FTrukhacev
        00.00.0000 00:00

        Спасибо большое за ответ и направление!)