Skip to content
This feature requires ACT Pro. Compare plans →

Robot Testing

Write stable, maintainable Flutter widget tests using the robot testing pattern. The /flutter-robot-testing skill generates tests that use key-based selectors, dependency injection, and deterministic fakes.

You have a multi-step user flow (e.g., sign-in, browse products, add to cart, checkout) and you want widget tests that verify the entire journey without relying on fragile finders like find.text('Submit').

  • ACT Pro installed and verified (/act:help)
  • A Flutter project with at least one user-facing screen implemented
  • Widget keys defined on interactive elements (buttons, text fields, etc.)
  • Familiarity with Flutter widget testing basics

Standard widget tests mix finder logic, interaction steps, and assertions into a single function. This makes them brittle and hard to read. The robot pattern extracts interactions into a robot class that reads like a user story:

final r = SignInRobot(tester);
await r.enterEmail('user@example.com');
await r.enterPassword('password123');
await r.tapSignInButton();
r.expectDashboardVisible();
Terminal window
/flutter-robot-testing

The skill loads robot testing rules covering key-first selectors, DI/fake setup, and risk reporting conventions.

Tell the AI which flow to test:

Terminal window
"Test the sign-in flow: enter email, enter password, tap sign in, verify dashboard appears"

3. Set up test seams with dependency injection

Section titled “3. Set up test seams with dependency injection”

The AI creates (or reuses) fakes for external dependencies:

class FakeAuthRepository extends Fake implements AuthRepository {
@override
Future<User> signIn(String email, String password) async {
return User(id: '1', email: email);
}
}

These fakes are injected via your app’s DI system (Provider, Riverpod, get_it, etc.) so the test never hits real services.

The AI creates a robot class with methods for each interaction step:

class SignInRobot {
SignInRobot(this.tester);
final WidgetTester tester;
Future<void> enterEmail(String email) async {
final field = find.byKey(const Key('signInEmailField'));
await tester.enterText(field, email);
await tester.pump();
}
Future<void> tapSignInButton() async {
final button = find.byKey(const Key('signInButton'));
await tester.tap(button);
await tester.pumpAndSettle();
}
void expectDashboardVisible() {
expect(find.byKey(const Key('dashboardScreen')), findsOneWidget);
}
}

All finders use find.byKey for stability. No find.text or find.byType for interactive elements.

testWidgets('sign in navigates to dashboard', (tester) async {
await tester.pumpWidget(makeTestApp(
authRepository: FakeAuthRepository(),
));
final r = SignInRobot(tester);
await r.enterEmail('user@example.com');
await r.enterPassword('password123');
await r.tapSignInButton();
r.expectDashboardVisible();
});

The AI runs flutter test to confirm the test passes, then reports any risks or coverage gaps it identified during generation.

Add more test cases for the same flow (invalid credentials, empty fields, network errors) and extend to other user journeys by creating new robot classes.

  • One robot class per screen or flow under test
  • Widget tests that read like user stories
  • All finders using stable key-based selectors
  • Fakes for all external dependencies (no real network calls, no real databases)
  • flutter test passing with the new tests
  • Missing widget keys — If your widgets do not have keys, the AI will flag this. Add keys to interactive elements before generating robot tests.
  • Using find.text for buttons — Text changes break tests. Always use find.byKey for elements users interact with.
  • Testing implementation details — Robot tests should verify user-visible outcomes, not internal state. Check what the user sees, not what the repository stores.
  • Non-deterministic fakes — Fakes must return consistent results. Avoid random data or timestamps in test fakes.