bloc_testmate 1.0.7
bloc_testmate: ^1.0.7 copied to clipboard
Package that streamlines testing for BLoCs. tiny test-oriented DI container, not boilerplate.Supports data-driven tests, helpful matchers, and async-friendly waits.
BlocTestMate π§ͺ #
Scenario-oriented testing utilities for BLoC π―.
Package Overview π§ #
BlocTestMate
is a Dart package that helps you write expressive, scenario-based tests for your BLoC classes. Built on top of bloc
and bloc_test
, it lets you arrange dependencies, define actions, and assert on emitted states with minimal boilerplate. The package includes a lightweight registry for mocks, hooks for setup/teardown and helpers for data-driven testing.
Features β¨ #
- Define BLoC test scenarios in 1β2 lines with
scenario
. - Lightweight dependency registry to register and override fakes per test.
- Global and per-scenario hooks for
setUp
andtearDown
. - Data-driven tests with the
table
helper. - Convenience re-exports for common matchers.
- Golden-state testing via
golden
files. - CLI: Generate placeholder tests for discovered
Bloc<_, _>
classes.
Why BlocTestMate instead of bloc_test
? #
BlocTestMate builds on bloc_test
, but adds utilities that reduce manual work when writing BLoC tests:
- Scenario DSL lets you define a test case in one or two lines and orchestrate dependencies and actions with minimal code.
- Lightweight registry isolates mocks and fakes between scenarios, with global or per-scenario hooks for
setUp
andtearDown
. - Native support for parameterized tests with
table
and for golden-state testing of state sequences.
Using bloc_test
alone requires assembling all this infrastructure yourself (dependency registry, parameterization, golden-state handling), which leads to more boilerplate and a risk of leaking mocks. BlocTestMate automates these tasks, making complex scenario testing easier.
Installation π¦ #
Add the package as a dev dependency to your pubspec.yaml
:
dev_dependencies:
bloc_testmate: ^1.0.0
or run:
flutter pub add bloc_testmate --dev
Then install the dependencies:
dart pub get
flutter pub get
Usage π #
Running tests βΆοΈ #
Run the test suite to verify that your BLoC scenarios behave as expected.
dart test
If your project uses Flutter, run:
flutter test
These tests check state transitions, golden snapshots, and guard against regressions in your BLoCs.
1. Define a simple scenario π #
import 'package:bloc_testmate/bloc_testmate.dart';
void main() {
final mate = BlocTestMate<LoginBloc, LoginState>()
.arrange((get) {
get.register<AuthRepo>(FakeAuthRepo(success: true));
})
.factory((get) => LoginBloc(get<AuthRepo>()));
mate.scenario(
'login ok',
given: () => [CredentialsEntered('a@a.com', '1234')],
when: (bloc) => bloc.add(SubmitPressed()),
wait: const Duration(milliseconds: 20),
expectStates: [isA<LoginLoading>(), isA<LoginSuccess>()],
);
}
πCLI: Generate placeholder tests π οΈ #
BlocTestMate includes a small CLI that scans your project for Bloc<_, _>
classes and generates starter test files.
- Create a
bloc_testmate.yaml
at the project root:
include:
- lib/**
exclude:
- lib/generated/**
output: test
- Run the generator:
dart run bloc_testmate generate --config bloc_testmate.yaml
This creates placeholder tests under the configured output
directory (by
default test/
). Each file includes a success and error scenario using
BlocTestMate
.
Running without a config file βΉοΈ
If you omit --config
or the file does not exist, the CLI uses sensible
defaults:
- include: all Dart files under the current directory
- exclude: none
- output:
test
Example:
dart run bloc_testmate generate
With these defaults, the CLI scans the current project for classes extending
Bloc<_, _>
and generates starter tests into test/
.
2. Data-driven testing with table
π #
table(
'create combinations',
rows: const [
{'id': '10', 'title': 'A'},
{'id': '11', 'title': 'B'},
],
build: (row) {
mate.scenario(
'create id=${row['id']}',
when: (bloc) =>
bloc.add(CreateTodo(row['id'] as String, row['title'] as String)),
wait: const Duration(milliseconds: 20),
expectStates: [
isA<TodoLoading>(),
predicate<TodoState>(
(s) => s is TodoLoaded &&
s.items.any(
(t) => t.id == row['id'] && t.title == row['title'],
),
),
],
);
},
);
3. Golden testing πΈ #
BlocTestMate
can persist state sequences to JSON "golden" files so that
future runs can verify behaviour hasn't changed.
Pass a golden
filename to mate.scenario
to automatically log the states
and compare them against a file stored under test/goldens/
:
mate.scenario(
'loads todos matches golden',
when: (bloc) => bloc.add(LoadTodos()),
wait: const Duration(milliseconds: 20),
golden: 'todo_success.json',
);
You can also use GoldenLogger
directly for fine-grained control:
final bloc = TodoBloc(FakeTodoRepo());
final logger = GoldenLogger<TodoState>(bloc);
bloc.add(LoadTodos());
await Future<void>.delayed(const Duration(milliseconds: 20));
await logger.expectMatch('test/goldens/todo_success.json');
await bloc.close();
If you don't call expectMatch
, make sure to dispose the logger when it's no
longer needed:
final logger = GoldenLogger<TodoState>(bloc);
// ...
await logger.dispose();
Custom matchers π§© #
BlocTestMate
ships with matchers that make stream expectations concise.
emitsInOrderStates
#
Verifies that a stream emits the provided states in order and then completes.
final stream = Stream.fromIterable([1, 2]);
await expectLater(stream, emitsInOrderStates([1, 2, noMoreStates()]));
emitsWhere
#
Matches the next emitted state against a custom predicate.
final stream = Stream.value(42);
await expectLater(stream, emitsWhere((s) => s == 42));
noMoreStates
#
Asserts that a stream emits no further values and closes.
final stream = Stream<int>.empty();
await expectLater(stream, noMoreStates());
Parameters βοΈ #
arrange
: Override or add fakes for a scenario.given
: Events dispatched before the action.when
: Action executed on the bloc.expectStates
: Expected states or matchers.expectInitialState
: Matcher for the initial state.errors
: Expected errors.golden
: JSON file used to compare emitted state sequences.setUp
/tearDown
: Hooks executed before/after each scenario.wait
: Delay before assertions.
Windows-friendly globbing πͺ #
The CLI uses POSIX-style glob matching internally and normalizes file paths so
that patterns like **/*.dart
and **/ignore_bloc.dart
also match files at
the repository root on Windows.
License π #
This package is open source under the MIT license. See LICENSE for details.