Skip to main content

Test your Telegram bots easily

Project description

aiogram_bot_tester

aiogram telegram status python

Navigation / Навигация - English version - Русская версия

A lightweight testing utility for offline testing of aiogram bots without real Telegram API calls.

Its goal is to make it easy to test bot logic deterministically by simulating Telegram updates, intercepting bot API calls, and exposing a clean assertion-friendly response layer.

Легковесная библиотека для оффлайн тестирования ботов, написанных на aiogram без использования интернета и Telegram API.

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

Installation / Установка

Stable version / Стабильная версия:

pip install aiogram_bot_tester

Latest development version / Последняя разрабатываемая версия

pip install git+https://github.com/samedit66/aiogram-bot-tester.git

[!IMPORTANT] aiogram_bot_tester additionaly installs pytest-asyncio for async tests. The library code itself doesn't depend on that package, it is just for convinience.

Quick example / Пример

import aiogram
from aiogram import filters, types
from aiogram_bot_tester import BotTester
import pytest

router = aiogram.Router()

@router.message(filters.CommandStart())
async def cmd_start(message: types.Message) -> None:
    await message.answer("Hello! What's your name?")

@pytest.mark.asyncio
async def test_it() -> None:
    tester = BotTester.from_routers(router)

    # For simpler cases, when you have just a ``Bot`` and a ``Dispatcher`:
    # tester = BotTester(bot=bot, dispatcher=dispatcher)

    response = await tester.send_command("start")
    assert response.contains("Hello")

English Version

Core API

BotTester.from_routers(*routers)

Construct a BotTester instance from a sequence of Routerss.

tester = BotTester.from_routers(router)

send_message(text, **kwargs)

Send a message to the bot. Returns a special Response object described below.

response = await tester.send_message("/start")
assert response.contains("Hello")

send_command(command, *command_args, prefix="/")

Helper to make sending commands more easily.

Instead of

await tester.send_message(f"/sum {a} {b}")

you can simply write

await tester.send_command("sum", a, b)

click_reply_button(text, **kwargs)

Simulates clicking a reply button. Actually, it's just another way of calling send_message. Exists just to clarify intention.

response = await tester.click_reply_button("Option A")

click_inline_button(label)

Simulates clicking an inline button.

response = await tester.click_inline_button("Press")

Response

Each interaction with a BotTester returns a Response object, which represents answer generated by a Telegram bot.

contains(*texts)

Returns True if this response text contains any of the given texts, False otherwise.

Example:

assert response.contains("Hello")

matches(*regexes)

Returns True if this response text matches any of the given regexes, False otherwise. regexes may be a list of either strings or compiled patterns.

Example:

assert response.matches("\d+")

has_inline_button(label)

Returns True if this response has an inline button with label, False otherwise.

Example:

assert response.has_inline_button("Click me!")

has_reply_button(label)

Returns True if this response has a button with label, False otherwise.

Example:

assert response.has_reply_button("Click me!")

has_inline_keyboard_like(keyboard)

Returns True if this response has the specified inline keyboard, False otherwise.

Example:

assert response.has_inline_keyboard_like([
    ["Yes", "No"],
    ["Cancel"],
])

has_reply_keyboard_like(keyboard)

Returns True if this response has the specified reply keyboard, False otherwise.

Example:

assert response.has_reply_keyboard_like([
    ["1", "2", "3"],
    ["4", "5", "6"],
])

in_state(state)

Returns True if after this response the state is state, False otherwise.

Example:

assert response.in_state(Form.name)

storage_has(**kwargs)

Returns True when all of the given kwargs are present in storage.

Example:

assert response.storage_has(name="Bob")

🚀 Full example

import pytest
from aiogram import Router, F
from aiogram.types import Message, CallbackQuery
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup

from aiogram_bot_tester import BotTester

router = Router()

@router.message()
async def echo(message: Message):
    await message.answer(
        f"Echo: {message.text}",
        reply_markup=InlineKeyboardMarkup(
            inline_keyboard=[
                [InlineKeyboardButton(text="Press me", callback_data="press")]
            ]
        ),
    )

@router.callback_query(F.data == "press")
async def on_press(callback: CallbackQuery):
    await callback.message.answer("Button clicked!")

@pytest.mark.asyncio
async def test_full_flow():
    tester = BotTester.from_routers(router)

    response = await tester.send_message("Hello")
    assert response.contains("Echo: Hello")

    response = await tester.click_inline_button("Press me")
    assert response.contains("Button clicked!")

Русская версия


Core API

BotTester.from_routers(*routers)

Создает объект BotTester из набора роутеров.

tester = BotTester.from_routers(router1, router2, router3)

send_message(text, **kwargs)

Симулирует отправку сообщения боту. Возвращает специальный объект Response, рассматриваемый далее.

response = await tester.send_message("/start")
assert response.contains("Привет")

send_command(command, *command_args, prefix="/")

Вспомогательная функция для более легкой отправки команд.

Вместо

await tester.send_message(f"/sum {a} {b}")

можно просто писать

await tester.send_command("sum", a, b)

click_reply_button(text, **kwargs)

Симулирует нажатие на reply-кнопку. На самом деле, это просто синоним для метода send_message, но с названием отражающим суть.

response = await tester.click_reply_button("Вариант А")

click_inline_button(label)

Симулирует нажатие на inline-кнопку.

response = await tester.click_inline_button("Нажми")

Response

В результате любого взаимодействия с BotTester, возвращается объект Response - ответ, генерируемый Telegram-ботом.

contains(*texts)

Возвращает True, если текст ответа содержит хотя бы одну из указанных подстрок, иначе False.

Пример:

assert response.contains("Привет")

matches(*regexes)

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

Пример:

assert response.matches("\d+")

has_inline_button(label)

Возвращает True, если в ответе есть inline-кнопка с указанным текстом.

Привет:

assert response.has_inline_button("Нажми меня!")

has_reply_button(label)

Возвращает True, если в ответе есть reply-кнопка с указанным текстом.

Example:

assert response.has_reply_button("Нажми меня!")

has_inline_keyboard_like(keyboard)

Возвращает True, если inline-клавиатура совпадает с заданной структурой.

Example:

assert response.has_inline_keyboard_like([
    ["Да", "Нет"],
    ["Отмена"],
])

has_reply_keyboard_like(keyboard)

Возвращает True, если reply-клавиатура совпадает с заданной структурой.

Example:

assert response.has_reply_keyboard_like([
    ["1", "2", "3"],
    ["4", "5", "6"],
])

in_state(state)

Возвращает True, если бот находится в заданном состоянии.

Пример:

assert response.in_state(Form.name)

storage_has(**kwargs)

Возвращает True, если все переданные пары внутри хранилища данных состояния.

Пример:

assert response.storage_has(name="Bob")

🚀 Полный пример теста

import pytest
from aiogram import Router, F
from aiogram.types import Message, CallbackQuery
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup

from aiogram_bot_tester import BotTester

router = Router()

@router.message()
async def echo(message: Message):
    await message.answer(
        f"Echo: {message.text}",
        reply_markup=InlineKeyboardMarkup(
            inline_keyboard=[
                [InlineKeyboardButton(text="Нажми меня!", callback_data="press")]
            ]
        ),
    )

@router.callback_query(F.data == "press")
async def on_press(callback: CallbackQuery):
    await callback.message.answer("Кнопка нажата!")

@pytest.mark.asyncio
async def test_full_flow():
    tester = BotTester.from_routers(router)

    response = await tester.send_message("Hello")
    assert response.contains("Echo: Hello")

    response = await tester.click_inline_button("Нажми меня!")
    assert response.contains("Кнопка нажата!")

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiogram_bot_tester-0.4.0.tar.gz (7.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiogram_bot_tester-0.4.0-py3-none-any.whl (8.2 kB view details)

Uploaded Python 3

File details

Details for the file aiogram_bot_tester-0.4.0.tar.gz.

File metadata

  • Download URL: aiogram_bot_tester-0.4.0.tar.gz
  • Upload date:
  • Size: 7.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for aiogram_bot_tester-0.4.0.tar.gz
Algorithm Hash digest
SHA256 9405475f49a95d37232383ce4d32b671e52f1553f123024470cde0fcc47cd705
MD5 702c4614192b3266d73476756bb9aee6
BLAKE2b-256 4c4c6bf3d1d7c8c619a3e54cbe68294748150593eafc51577e15dfb63c5bb553

See more details on using hashes here.

File details

Details for the file aiogram_bot_tester-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: aiogram_bot_tester-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 8.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for aiogram_bot_tester-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 953b447c3059d4b84750ec2111ced5d5e535d3d9e091d64b2c03f8f75672c0ce
MD5 374c31dae515c42b213e8d7492fdf932
BLAKE2b-256 fb2f10fe64f74d5e776b4dce331f344ede2e9716b8188ac03bd0c7e87007388f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page