Skip to content

Proposal: Testing framework for Bubble Tea applications (charm-test) #1654

@junhinhow

Description

@junhinhow

Problem

There is no standard way to test Bubble Tea applications. Developers either:

  • Test manually (slow, not CI-friendly)
  • Call Update() manually and inspect model internals (brittle, verbose)
  • Skip testing TUI logic entirely

Other UI frameworks have dedicated testing tools (React Testing Library, Flutter widget tests), but Bubble Tea has none.

Proposed Solution

charm-test — a testing framework that simulates the Bubble Tea runtime without a real terminal.

func TestLoginForm(t *testing.T) {
    sim := charmtest.New(NewLoginModel())

    sim.Type("user@email.com")
    sim.SendKey("tab")
    sim.Type("password123")
    sim.SendKey("enter")

    charmtest.RequireViewContains(t, sim, "Welcome")
    charmtest.RequireSnapshot(t, sim)  // golden file comparison
}

Features

  • Simulator — drives Model synchronously through Init/Update/View (no goroutines, deterministic)
  • Input simulationSendKey("enter"), Type("hello"), Resize(80, 24)
  • AssertionsRequireView(), RequireViewContains(), RequireViewLines() with automatic ANSI stripping
  • Snapshot testing — golden file comparison with CHARM_TEST_UPDATE=1 to refresh
  • DebuggingDumpView(), DumpMessages() for test failure diagnosis

Design Principles

  • Deterministic: same input → same output, always
  • Fast: microseconds per test, not seconds
  • Simple: one import, one constructor
  • ANSI-aware: all comparisons strip escape sequences

Working POC

https://github.com/junhinhow/charm-test

Includes a complete counter example with 5 test cases. Bilingual docs (EN/PT-BR).

Questions

  1. Would this fit as an official package (e.g. charm.land/bubbletea/v2/test)?
  2. Or better as a standalone community package?
  3. Should the Simulator also handle async commands (timers, HTTP) via a mock clock?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions