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
100%
Does it follow best practices?
Impact
97%
1.56xAverage score across 3 eval scenarios
Passed
No known issues
"""End-to-end checks that the plugin wires up through entry_points.
Each test runs a fresh inline pytest session via pytester so the assertions
verify what a real user sees after ``pip install pytest-conversational``:
fixtures resolve without an explicit import, and the ``conversational``
marker is registered cleanly under ``--strict-markers``.
"""
def test_conversation_fixture_is_discovered(pytester):
"""The ``conversation`` fixture must be available with zero imports."""
pytester.makepyfile(
"""
def test_uses_fixture(conversation):
assert conversation.turns == []
conversation.add_user("hello")
assert conversation.turns[-1].user == "hello"
"""
)
result = pytester.runpytest("-q")
result.assert_outcomes(passed=1)
def test_conversation_factory_fixture_is_discovered(pytester):
"""The ``conversation_factory`` fixture builds adapters on demand."""
pytester.makepyfile(
"""
def test_uses_factory(conversation_factory):
convo = conversation_factory(bot=lambda text, c: f"echo: {text}")
convo.say("ping")
assert convo.last.bot == "echo: ping"
"""
)
result = pytester.runpytest("-q")
result.assert_outcomes(passed=1)
def test_conversational_marker_does_not_warn_under_strict(pytester):
"""``@pytest.mark.conversational`` must resolve with no PytestUnknownMarkWarning."""
pytester.makepyfile(
"""
import pytest
@pytest.mark.conversational
def test_marked(conversation):
conversation.add_user("hi")
assert len(conversation.turns) == 1
"""
)
result = pytester.runpytest("-q", "--strict-markers", "-W", "error")
result.assert_outcomes(passed=1)
def test_conversational_marker_filters_collection(pytester):
"""``pytest -m conversational`` selects only marked tests."""
pytester.makepyfile(
"""
import pytest
@pytest.mark.conversational
def test_picked():
assert True
def test_skipped():
assert True
"""
)
result = pytester.runpytest("-q", "-m", "conversational")
result.assert_outcomes(passed=1, deselected=1).tessl-plugin
evals
src
pytest_conversational
tests