Skip to content

loonix/self_test

Repository files navigation

self_test

pub package

Automated regression testing for Flutter through direct callback invocation. Unlike traditional testing frameworks that simulate UI interactions, self_test invokes widget callbacks directly, providing fast and reliable testing for development and pre-production environments.

🤖 AI-Powered Testing: Pair with self_test_mcp to enable Claude and other AI agents to test your Flutter apps with Playwright feature parity - works on iOS, Android, Web, and Desktop!

Features

Core Testing

  • Direct Callback Invocation - Execute user actions by calling widget callbacks directly instead of injecting touch events
  • Runtime Testing - Run tests in live app environments without external test frameworks
  • Text Assertions - Built-in text validation for input fields
  • Memory Safe - Automatic registration/unregistration prevents memory leaks
  • Hot Restart Compatible - Works seamlessly with Flutter's hot restart
  • Test Scenarios - Define and run multi-step test scenarios with TestScenario

AI-Powered Testing (with MCP)

  • 60+ Playwright-Equivalent Tools - Full feature parity with Playwright browser testing
  • State Management Inspection - Inspect and modify Riverpod, Bloc, and Provider state
  • Network Mocking - Mock HTTP responses, block requests, monitor traffic
  • Visual Regression - Golden file comparison for UI testing
  • Platform Mocking - Mock GPS, permissions, platform channels, sensors
  • Cross-Platform - Works on iOS, Android, Web (with or without bridge), Desktop
  • Bridgeless Web Testing - Test any Flutter web app via Playwright + semantics tree

Installation

Core Package Only

Add to your pubspec.yaml:

dependencies:
  self_test: ^0.1.0

Then run:

flutter pub get

With AI-Powered Testing (Optional)

For AI agent integration with Claude Code:

  1. Install MCP server:

    git clone https://github.com/loonix/self_test
    cd self_test/packages/self_test_mcp
    npm install && npm run build
    ./scripts/setup-claude.sh
  2. Add bridge to your app:

    dependencies:
      self_test_bridge:
        git:
          url: https://github.com/loonix/self_test
          path: packages/self_test_bridge

See Architecture section below for details.

Sponsors

Objais
Proudly sponsored by Objais

Quick Start

1. Wrap Your App Root

import 'package:self_test/self_test.dart';

void main() {
  runApp(SelfTestRoot(child: MyApp()));
}

2. Wrap Interactive Widgets

SelfTestableWidget(
  id: 'username_field',
  onTextChange: (value) => setState(() => username = value),
  child: TextField(
    decoration: InputDecoration(labelText: 'Username'),
    onChanged: (value) => setState(() => username = value),
  ),
),

3. Run Tests

SelfTestManager().enterText('username_field', 'john_doe');
SelfTestManager().trigger('login_button');
await SelfTestManager().waitForAnimations();

Usage

Code Generation with Annotations

For type-safe test controllers, annotate your callback methods:

import 'package:self_test/self_test.dart';

part 'login_form.self_test.g.dart';

class _LoginFormState extends State<LoginForm> {
  @SelfTestButton('login_btn')
  void onLoginPressed() { /* ... */ }

  @SelfTestInput('username_field')
  void onUsernameChanged(String value) { /* ... */ }

  @SelfTestInput('password_field')
  void onPasswordChanged(String value) { /* ... */ }
}

Generate the controller:

flutter pub run build_runner build

Use the generated controller:

final controller = LoginFormTestController();

controller.enterUsernameField('john_doe');
controller.enterPasswordField('secret123');
controller.tapLoginBtn();
controller.expectUsernameFieldText('john_doe');

Test Scenarios

Define multi-step test scenarios:

final scenario = TestScenario(
  name: 'Login flow',
  steps: [
    TestStep.enterText('username_field', 'user@example.com'),
    TestStep.enterText('password_field', 'password123'),
    TestStep.tap('login_button'),
    TestStep.wait(Duration(milliseconds: 500)),
    TestStep.screenshot('after_login'),
  ],
);

final result = await scenario.run();
print(result.allPassed ? 'All steps passed' : 'Failed at step ${result.failedAtStep}');

Widget Testing

Integrate with flutter_test:

testWidgets('Login flow test', (WidgetTester tester) async {
  SelfTestManager().setTestMode(true);

  await tester.pumpWidget(MyApp());
  await tester.pumpAndSettle();

  SelfTestManager().enterText('username_field', 'testuser');
  SelfTestManager().trigger('login_button');
  await tester.pump();

  expect(find.text('Login successful!'), findsOneWidget);
});

Enabling Self-Test Mode

// In debug/profile builds
SelfTestManager().setSelfTestModeActive(true);
SelfTestManager().restartWidgetTree();

// In test environments
SelfTestManager().setTestMode(true);

API Reference

SelfTestManager

Singleton managing test nodes and actions.

Method Description
trigger(id) Tap a button by ID
enterText(id, text) Enter text in a field by ID
waitForAnimations() Wait for UI updates
restartWidgetTree() Force widget tree rebuild
captureScreenshot([name]) Capture a screenshot
registerTestNode(node) Register a test node
unregisterTestNode(id) Unregister a test node
setSelfTestModeActive(bool) Enable/disable in debug/profile
setTestMode(bool) Enable/disable in test environments

SelfTestableWidget

SelfTestableWidget({
  required String id,
  required Widget child,
  VoidCallback? onTap,
  ValueSetter<String>? onTextChange,
})

Annotations

@SelfTestButton(String id)  // For tappable widgets
@SelfTestInput(String id)   // For text input widgets

Architecture

The self_test ecosystem consists of three components:

┌─────────────────────────────────────────────────────────────────┐
│                      AI Agent (Claude)                           │
└─────────────────────────────┬───────────────────────────────────┘
                              │ MCP Protocol
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                   self_test_mcp Server                           │
│  • 60+ Playwright-equivalent tools                               │
│  • Actions: tap, type, scroll, drag                             │
│  • Assertions: expect, visual regression                        │
│  • State inspection: Riverpod, Bloc, Provider                   │
│  • Network mocking & monitoring                                 │
└─────────────────────────────┬───────────────────────────────────┘
                              │ WebSocket
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                Flutter App + self_test_bridge                    │
│  • Receives commands from MCP                                    │
│  • Executes via self_test callbacks                             │
│  • Works on iOS, Android, Web, Desktop                          │
└─────────────────────────────────────────────────────────────────┘

Components

Package Description When to Use
self_test (this package) Core testing framework with direct callback invocation Always - enables runtime testing in your Flutter app
self_test_bridge WebSocket bridge connecting MCP to Flutter When using AI-powered testing with Claude
self_test_mcp MCP server with 60+ tools for AI agents When using AI agents for automated testing

AI-Powered Testing with MCP

The self_test MCP (Model Context Protocol) server enables AI agents like Claude to test your Flutter apps with Playwright feature parity.

Platform Support

Platform Bridge Required How It Works
iOS ✅ Yes Bridge runs WebSocket server on device, MCP connects
Android ✅ Yes Bridge runs WebSocket server on device, MCP connects
Web (with bridge) ✅ Yes Bridge embedded in web app, MCP connects
Web (bridgeless) ❌ No MCP uses Playwright + Flutter semantics tree
Desktop ✅ Yes Bridge runs WebSocket server in app, MCP connects

Quick Start with MCP

1. Install MCP Server

cd packages/self_test_mcp
npm install && npm run build

# Add to Claude Code automatically
./scripts/setup-claude.sh

Or manually add to ~/.claude/settings.json:

{
  "mcpServers": {
    "flutter-self-test": {
      "command": "node",
      "args": ["/path/to/self_test_mcp/dist/index.js"],
      "env": {
        "FLUTTER_APP_HOST": "localhost",
        "FLUTTER_APP_PORT": "9999"
      }
    }
  }
}

2. Add Bridge to Flutter App

Add to pubspec.yaml:

dependencies:
  self_test_bridge:
    git:
      url: https://github.com/loonix/self_test
      path: packages/self_test_bridge

Add to main.dart:

import 'package:flutter/foundation.dart';
import 'package:self_test_bridge/self_test_bridge.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Start bridge in debug mode only
  if (kDebugMode) {
    final bridge = SelfTestBridge(port: 9999);
    await bridge.start();
  }

  runApp(SelfTestRoot(child: MyApp()));
}

3. Test with Claude

Open Claude Code and ask:

Test the login flow in my Flutter app:
1. Enter username "test@example.com"
2. Enter password "password123"
3. Tap login button
4. Verify we navigated to the dashboard

Claude will use the MCP tools to interact with your app!

Web-External Mode (No Bridge Required!)

Test any Flutter web app without code changes using Playwright:

# Configure for web-external mode
export BRIDGE_MODE=web-external
export FLUTTER_APP_URL=https://your-app.com
export PLAYWRIGHT_HEADLESS=false

# Run MCP server
npm start

Perfect for:

  • Production web apps
  • Third-party Flutter apps
  • CI/CD smoke tests
  • Quick exploratory testing

Available MCP Tools

The MCP server provides 60+ tools with Playwright feature parity:

Locators & Queries:

  • flutter_snapshot - Get widget tree
  • flutter_get_by_role - Find by semantic role
  • flutter_get_by_text - Find by text content

Actions:

  • flutter_tap, flutter_type, flutter_clear, flutter_scroll
  • flutter_drag, flutter_hover, flutter_focus
  • flutter_long_press, flutter_double_tap

Assertions:

  • flutter_expect - Assert widget state (toBeVisible, toHaveText, etc.)
  • flutter_expect_screenshot - Visual regression testing

State Management:

  • flutter_get_state - Inspect Riverpod/Bloc/Provider state
  • flutter_dispatch_action - Dispatch events/actions
  • flutter_watch_state - Subscribe to state changes

Network:

  • flutter_mock_http - Mock API responses
  • flutter_block_http - Block requests
  • flutter_network_log - Monitor network traffic

Platform Mocking:

  • flutter_set_geolocation - Mock GPS
  • flutter_set_permission - Mock permissions
  • flutter_mock_channel - Mock platform channels

[See full tool list in packages/self_test_mcp/README.md]

Ecosystem

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Run the test suite: flutter test
  6. Submit a pull request

License

Copyright (c) 2025-2026 Ari Silva, Daniel Carneiro. All rights reserved.

This software may be used and modified in your own products and services, but may not be sold or redistributed as a standalone product. See the LICENSE file for details.

About

Automated regression testing for Flutter through direct callback invocation. Test your Flutter apps with AI agents (Claude) using 60+ Playwright-equivalent tools. Works on iOS, Android, Web, and Desktop. No UI simulation needed, reliable testing.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors