CtrlK
BlogDocsLog inGet started
Tessl Logo

golikovichev/pytest-conversational

Test chat bots, voice assistants, and IVR menus with pytest using a small Conversation object and a callable bot adapter. Use when the user wants to write rule-based assertions over multi-turn dialogue without bringing in an LLM dependency, when they have a chatbot reachable as a Python callable or HTTP webhook, when they need to keep per-conversation state across turns and assert on slot filling, when they want pytest-native fixtures and a printable transcript on failure, or when they mention voice-assistant testing, IVR menu testing, conversational AI testing, LLM bot testing (used as the target under test, not as the matcher), expect matchers for bot replies, or multi-turn dialogue tests.

99

1.56x
Quality

100%

Does it follow best practices?

Impact

97%

1.56x

Average score across 3 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

test_weather_bot.pyexamples/

"""Weather bot example.

Shows multi-turn state management with ``convo.state`` plus slot filling:
the bot asks for a city when the first message omits it and reads the
follow-up reply as the slot value.
"""

from pytest_conversational import expect


def weather_bot(text, convo):
    """A bot that asks for a city if not provided in the first turn."""
    state = convo.state
    text = text.lower()

    if "weather" in text:
        if "in" in text:
            city = text.split("in")[-1].strip()
            state["city"] = city
            return f"The weather in {city} is sunny."
        return "Which city are you interested in?"

    # Check previous turns for context
    if len(convo.turns) > 1 and "Which city" in convo.turns[-2].bot:
        state["city"] = text
        return f"The weather in {text} is sunny."

    return "I only know about the weather. Ask me 'weather in London'."


def test_weather_flow(conversation_factory):
    convo = conversation_factory(bot=weather_bot)

    # Turn 1: Partial request
    convo.say("what is the weather?")
    assert "Which city" in convo.last.bot

    # Turn 2: Providing the slot
    convo.say("London")
    assert convo.state["city"] == "london"
    expect.contains(convo.last.bot, "sunny")
    expect.contains(convo.last.bot, "London")


def test_weather_direct(conversation_factory):
    convo = conversation_factory(bot=weather_bot)

    # Turn 1: Full request
    convo.say("weather in Paris")
    assert convo.state["city"] == "paris"
    expect.contains(convo.last.bot, "Paris")

CHANGELOG.md

CONTRIBUTING.md

README.md

REFERENCE.md

SECURITY.md

SKILL.md

tessl.json

tile.json