flutter-testing
Flutter testing: Widget Tests (testWidgets, WidgetTester, pump, pumpAndSettle, finders), Integration Tests (flutter_test driver), mocking with mocktail, Golden File Tests (visual regression, --update-goldens), BLoC testing (bloc_test package), coverage measurement and CI integration.
Best use case
flutter-testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Flutter testing: Widget Tests (testWidgets, WidgetTester, pump, pumpAndSettle, finders), Integration Tests (flutter_test driver), mocking with mocktail, Golden File Tests (visual regression, --update-goldens), BLoC testing (bloc_test package), coverage measurement and CI integration.
Teams using flutter-testing should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/flutter-testing/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How flutter-testing Compares
| Feature / Agent | flutter-testing | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Flutter testing: Widget Tests (testWidgets, WidgetTester, pump, pumpAndSettle, finders), Integration Tests (flutter_test driver), mocking with mocktail, Golden File Tests (visual regression, --update-goldens), BLoC testing (bloc_test package), coverage measurement and CI integration.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Flutter Testing Skill
Flutter provides three test levels built into the framework. This skill covers the patterns for each, from unit-testing pure Dart logic to visual regression golden tests.
## When to Activate
- Writing tests for a new Flutter widget or feature
- Setting up visual regression testing with golden files
- Testing BLoC state machines
- Debugging flaky widget tests
- Setting up CI test pipeline for Flutter
- Choosing the right test level (unit, widget, or integration) for a given piece of logic
- Mocking dependencies with mocktail when code generation from mockito is not desirable
- Adding code coverage gating to a Flutter CI pipeline with lcov and Codecov
---
## Test Pyramid
```
┌──────────────────────────┐
│ Integration Tests │ Few — slow, real device/emulator
│ (flutter_test driver) │
├──────────────────────────┤
│ Widget Tests │ Many — fast, no device needed
│ (testWidgets) │
├──────────────────────────┤
│ Unit Tests │ Most — pure Dart, fastest
│ (test package) │
└──────────────────────────┘
```
---
## Unit Tests
Pure Dart — no widgets, no Flutter framework.
```dart
// test/unit/domain/cart_service_test.dart
import 'package:test/test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:myapp/domain/services/cart_service.dart';
class MockCartRepository extends Mock implements CartRepository {}
void main() {
group('CartService.addItem', () {
late CartService service;
late MockCartRepository mockRepo;
setUp(() {
mockRepo = MockCartRepository();
service = CartService(repository: mockRepo);
registerFallbackValue(CartItem.empty());
});
test('adds item to cart', () async {
when(() => mockRepo.save(any())).thenAnswer((_) async {});
when(() => mockRepo.load()).thenAnswer((_) async => []);
final result = await service.addItem(Product(id: 'p1', price: 10.0));
expect(result, hasLength(1));
expect(result.first.productId, 'p1');
verify(() => mockRepo.save(any())).called(1);
});
test('throws CartFullException when cart has 100 items', () async {
when(() => mockRepo.load()).thenAnswer(
(_) async => List.generate(100, (_) => CartItem.empty()),
);
expect(
() => service.addItem(Product(id: 'p1', price: 10.0)),
throwsA(isA<CartFullException>()),
);
});
});
}
```
---
## Widget Tests
```dart
// test/widget/features/product/product_card_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('ProductCard', () {
testWidgets('displays product name and price', (tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: ProductCard(
product: Product(id: 'p1', name: 'Widget Pro', price: 49.99),
),
),
),
);
expect(find.text('Widget Pro'), findsOneWidget);
expect(find.text('\$49.99'), findsOneWidget);
});
testWidgets('calls onAddToCart when button tapped', (tester) async {
var tapped = false;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: ProductCard(
product: Product(id: 'p1', name: 'Test', price: 9.99),
onAddToCart: () => tapped = true,
),
),
),
);
await tester.tap(find.byKey(const Key('add_to_cart_button')));
await tester.pump();
expect(tapped, isTrue);
});
testWidgets('shows loading state during async operation', (tester) async {
await tester.pumpWidget(
const MaterialApp(home: CheckoutScreen()),
);
await tester.tap(find.text('Place Order'));
await tester.pump(); // Start the async operation
expect(find.byType(CircularProgressIndicator), findsOneWidget);
await tester.pumpAndSettle(); // Wait for async completion
expect(find.text('Order Confirmed'), findsOneWidget);
});
});
}
```
### Key Finders
```dart
find.byType(ProductCard) // by widget type
find.byKey(const Key('submit')) // by key (most stable)
find.text('Add to Cart') // by exact text
find.textContaining('Cart') // partial text
find.byIcon(Icons.shopping_cart) // by icon
find.descendant(of: find.byType(Card), matching: find.byType(Text))
find.ancestor(of: find.text('price'), matching: find.byType(Card))
```
### pump vs. pumpAndSettle
```dart
await tester.pump(); // one frame
await tester.pump(const Duration(seconds: 1)); // simulate time
await tester.pumpAndSettle(); // wait for all frames (animations, futures)
await tester.pumpAndSettle(const Duration(seconds: 5)); // with timeout
```
---
## mocktail
Preferred over mockito — no code generation required.
```dart
import 'package:mocktail/mocktail.dart';
// Create mock
class MockUserRepository extends Mock implements UserRepository {}
// Setup
setUp(() {
mock = MockUserRepository();
// Required for non-nullable types used as arguments
registerFallbackValue(User.empty());
});
// Stub
when(() => mock.findById('1')).thenReturn(User(id: '1', name: 'Alice'));
when(() => mock.findById('missing')).thenThrow(NotFoundException('1'));
when(() => mock.save(any())).thenAnswer((_) async {});
// Async stubs
when(() => mock.fetchAll())
.thenAnswer((_) async => [User(id: '1', name: 'Alice')]);
// Verify
verify(() => mock.findById('1')).called(1);
verifyNever(() => mock.delete(any()));
```
---
## BLoC Testing (bloc_test)
```dart
import 'package:bloc_test/bloc_test.dart';
void main() {
group('CartBloc', () {
late MockCartRepository repo;
setUp(() => repo = MockCartRepository());
blocTest<CartBloc, CartState>(
'emits [loading, withItem] when AddToCart succeeds',
build: () => CartBloc(repository: repo),
setUp: () => when(() => repo.addItem(any(), any()))
.thenAnswer((_) async => [CartItem(productId: 'p1')]),
act: (bloc) => bloc.add(AddToCart(product: Product(id: 'p1'))),
expect: () => [
const CartState(isLoading: true),
CartState(items: [CartItem(productId: 'p1')]),
],
);
blocTest<CartBloc, CartState>(
'emits [loading, error] when repository throws',
build: () => CartBloc(repository: repo),
setUp: () => when(() => repo.addItem(any(), any()))
.thenThrow(CartException('Out of stock')),
act: (bloc) => bloc.add(AddToCart(product: Product(id: 'p1'))),
expect: () => [
const CartState(isLoading: true),
const CartState(error: CartError('Out of stock')),
],
);
blocTest<CartBloc, CartState>(
'emits nothing when same item added twice rapidly',
build: () => CartBloc(repository: repo),
act: (bloc) => bloc
..add(AddToCart(product: Product(id: 'p1')))
..add(AddToCart(product: Product(id: 'p1'))),
expect: () => [/* whatever your deduplication logic produces */],
);
});
}
```
---
## Golden File Tests (Visual Regression)
```dart
// test/golden/product_card_golden_test.dart
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('ProductCard - default state', (tester) async {
await tester.binding.setSurfaceSize(const Size(400, 200));
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light,
home: Scaffold(
body: ProductCard(product: Product.sample()),
),
),
);
await expectLater(
find.byType(ProductCard),
matchesGoldenFile('goldens/product_card_default.png'),
);
});
}
```
```bash
# Create/update baseline images
flutter test --update-goldens test/golden/
# Run comparison (fail on visual diff)
flutter test test/golden/
# CI: always run comparison, never auto-update
```
**Golden test best practices:**
- Use fixed `Surface Size` for consistent output across machines
- Use deterministic data (no timestamps, random values)
- Commit golden files to git — they're your visual baseline
- Group golden tests separately from functional tests (different run frequency)
---
## Coverage
```bash
# Run tests with coverage
flutter test --coverage
# Coverage report in: coverage/lcov.info
# View HTML report
genhtml coverage/lcov.info --output-directory coverage/html
open coverage/html/index.html
# CI gate
lcov --summary coverage/lcov.info
# Check "lines......" percentage
```
```yaml
# .github/workflows/flutter.yml
- name: Test with coverage
run: flutter test --coverage
- name: Upload to Codecov
uses: codecov/codecov-action@v3
with:
file: coverage/lcov.info
```
---
## Testability Patterns
Make code testable from the start:
```dart
// WRONG: hardwired dependency
class OrderService {
final repo = OrderRepository(); // can't mock
// CORRECT: constructor injection
class OrderService {
const OrderService(this._repository);
final OrderRepository _repository;
}
// CORRECT: abstract interface for mocking
abstract class OrderRepository {
Future<Order> create(OrderRequest request);
}
class RemoteOrderRepository implements OrderRepository { /* ... */ }
class MockOrderRepository extends Mock implements OrderRepository {}
```
---
## Reference
- `rules/flutter/testing.md` — test conventions, tooling setup
- `skills/flutter-patterns` — BLoC/Riverpod architecture patterns
- `skills/dart-patterns` — sealed classes, Result pattern for error handlingRelated Skills
visual-testing
Visual Regression Testing: tool comparison (Chromatic/Percy/Playwright screenshots/BackstopJS), pixel-diff vs AI-based comparison, baseline management, flakiness strategies (masks, tolerances, waitForLoadState), CI integration with GitHub Actions, and Storybook integration.
typescript-testing
TypeScript testing patterns: Vitest for unit/integration, Playwright for E2E, MSW for API mocking, Testing Library for React components. Core TDD methodology for TypeScript/JavaScript projects.
swift-testing
Swift testing patterns: Swift Testing framework (Swift 6+), XCTest for UI tests, async/await test cases, actor testing, Combine testing, and XCUITest for UI automation. TDD for Swift/SwiftUI.
swift-protocol-di-testing
Protocol-based dependency injection for testable Swift code — mock file system, network, and external APIs using focused protocols and Swift Testing.
scala-testing
Scala testing with ScalaTest, MUnit, and ScalaCheck: FunSpec/FlatSpec test structure, property-based testing with forAll, mocking with MockitoSugar, Cats Effect testing with munit-cats-effect (runTest/IOSuite), ZIO Test, Testcontainers-Scala for database integration tests, and CI integration with sbt. Use when writing or reviewing Scala tests.
rust-testing
Rust testing patterns — unit tests with mockall, integration tests with sqlx transactions, HTTP handler testing (axum), benchmarks (criterion), property tests (proptest), fuzzing, and CI with cargo-nextest.
rust-testing-advanced
Advanced Rust testing anti-patterns and corrections — cfg(test) placement, expect() over unwrap(), mockall expectation ordering, executor mixing (#[tokio::test] vs block_on), PgPool isolation with
ruby-testing
RSpec testing patterns for Ruby and Rails — factories, mocks, request specs, feature specs, VCR, and SimpleCov coverage.
r-testing
R testing patterns: testthat 3e with expect_* assertions, snapshot testing, mocking with mockery and httptest2, covr code coverage, lintr static analysis, property-based testing with hedgehog, testing Shiny apps with shinytest2. Use when writing or reviewing R tests.
python-testing
Python testing strategies using pytest, TDD methodology, fixtures, mocking, and parametrization. Core testing fundamentals.
python-testing-advanced
Advanced Python testing — async testing with pytest-asyncio, exception/side-effect testing, test organization, common patterns (API, database, class methods), pytest configuration, and CLI reference. Extends python-testing.
php-testing
PHP testing patterns: PHPUnit 11 with mocks and data providers, Pest v3 with expectations and datasets, Laravel feature/HTTP tests with RefreshDatabase, Symfony WebTestCase, PHPStan static analysis, Infection mutation testing. Use when writing or reviewing PHP tests.