self_test 0.1.0
self_test: ^0.1.0 copied to clipboard
Automated regression testing for Flutter through direct callback invocation. Run tests in live app environments without UI simulation.
self_test #
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:
-
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 -
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 #
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 treeflutter_get_by_role- Find by semantic roleflutter_get_by_text- Find by text content
Actions:
flutter_tap,flutter_type,flutter_clear,flutter_scrollflutter_drag,flutter_hover,flutter_focusflutter_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 stateflutter_dispatch_action- Dispatch events/actionsflutter_watch_state- Subscribe to state changes
Network:
flutter_mock_http- Mock API responsesflutter_block_http- Block requestsflutter_network_log- Monitor network traffic
Platform Mocking:
flutter_set_geolocation- Mock GPSflutter_set_permission- Mock permissionsflutter_mock_channel- Mock platform channels
[See full tool list in packages/self_test_mcp/README.md]
Ecosystem #
Contributing #
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Run the test suite:
flutter test - 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.