Instruction file imported from mathiasjakobsen/breedelite (
.github/instructions/testing.instructions.md). Copyright stays with the author.
Testing Conventions
Test Structure
Mirror the lib/ directory structure under test/:
test/
features/
<feature>/
bloc/
<feature>_bloc_test.dart
widgets/
<widget>_test.dart
helpers/
pump_app.dart (PumpApp extension + mocks)
scaffold.dart (TestScaffold setup)
helpers.dart (display size helpers)
errors.dart (test exceptions)
Libraries
- bloc_test -- use
blocTest<>()for all BLoC tests - mocktail -- use
extends Mock implements Xfor mocking - flutter_test -- standard widget testing
BLoC Test Pattern
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
void main() {
group('FeatureBloc', () {
late MockSnackbarEventBus mockSnackbarBus;
late MockGlobalEventBus mockGlobalEventBus;
late FeatureBloc bloc;
setUp(() {
mockSnackbarBus = MockSnackbarEventBus();
mockGlobalEventBus = MockGlobalEventBus();
bloc = FeatureBloc(
snackbarBus: mockSnackbarBus,
globalEventBus: mockGlobalEventBus,
);
});
tearDown(() {
bloc.close();
});
test('initial state is correct', () {
expect(bloc.state, equals(const FeatureState()));
});
blocTest<FeatureBloc, FeatureState>(
'emits [loading, loaded] when InitFeatureEvent is added',
build: () => bloc,
act: (bloc) => bloc.add(const InitFeatureEvent()),
expect: () => [
const FeatureState(status: FeatureStatus.loading),
isA<FeatureState>()
.having((s) => s.status, 'status', FeatureStatus.loaded),
],
);
});
}
Mock Definitions
All mocks live in test/helpers/pump_app.dart (as a part 'mocks.dart' file):
class MockGateway extends Mock implements Gateway {}
class MockRouteBloc extends Mock implements RouteBloc {}
class MockGlobalBloc extends Mock implements GlobalBloc {}
class MockSnackbarEventBus extends Mock implements SnackbarEventBus {}
class MockGlobalEventBus extends Mock implements GlobalEventBus {}
Add new mocks to this file, not scattered across test files.
Test Helpers
PumpAppextension (pumpApp(),pumpRoutedApp()) -- wraps widgets with theme, localisation, event buses, and BLoC providersTestScaffold-- sets up IoC and gateway mocks- Display size helpers --
setLargeDisplaySize()etc. for responsive tests
Use tester.pumpApp(widget) instead of raw tester.pumpWidget().
Requirements
- Write
blocTestfor every BLoC event handler - Write widget tests for complex interactive widgets
- Always
setUpmocks andtearDownwithbloc.close() - Use
when()/verify()from mocktail, never write manual mocks - Test all state transitions: loading, loaded/success, error