Sparround

bloc_test: blocTest, MockBloc, whenListen

package:bloc_test turns bloc/cubit testing into a declarative form. The main function is blocTest, with these parameters:

  • `build` — constructs and returns the bloc under test.
  • `setUp` — prepares dependencies before the bloc is created.
  • `seed` — seeds an initial state into the bloc (starting from a populated list, say).
  • `act` — interacts with the bloc: adds events, calls cubit methods.
  • `wait` — a duration to wait for async work such as debounce.
  • `skip` — how many emitted states to skip before the assertions (default 0).
  • `expect` — the expected sequence of states (a matcher).
  • `verify` — extra assertions after expect (that the repository was called once, for instance).
  • `errors` — the expected exceptions.
  • `tearDown` — cleanup after the test.

Additional tools: `MockBloc`/`MockCubit` for creating a stub bloc, and `whenListen`, which creates a stub response for a bloc's listen method and keeps the state property in sync with the emitted values.

dart
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  late FakeTodoRepository repository;

  setUp(() {
    repository = FakeTodoRepository();
  });

  group('TodoBloc', () {
    blocTest<TodoBloc, TodoState>(
      'TodoStarted → loading, success ardıcıllığı yayımlanır',
      build: () => TodoBloc(repository),
      act: (bloc) => bloc.add(TodoStarted()),
      expect: () => [
        const TodoState(status: TodoStatus.loading),
        TodoState(status: TodoStatus.success, todos: repository.items),
      ],
      verify: (_) => expect(repository.fetchAllCallCount, 1),
    );

    blocTest<TodoBloc, TodoState>(
      'seed ilə dolu siyahıdan başlayıb element silir',
      build: () => TodoBloc(repository),
      seed: () => TodoState(
        status: TodoStatus.success,
        todos: const [Todo(id: '1', title: 'Süd al')],
      ),
      act: (bloc) => bloc.add(TodoDeleted('1')),
      expect: () => [
        const TodoState(status: TodoStatus.success, todos: []),
      ],
    );

    blocTest<TodoBloc, TodoState>(
      'iki dəfə göndərilən event droppable ilə bir dəfə işlənir',
      build: () => CheckoutBloc(repository) as dynamic,
      act: (bloc) => bloc
        ..add(CheckoutSubmitted(order))
        ..add(CheckoutSubmitted(order)),
      wait: const Duration(milliseconds: 100),
      verify: (_) => expect(repository.payCallCount, 1),
    );
  });
}

blocTest: the state sequence plus an extra check with `verify`.

dart
class MockTodoBloc extends MockBloc<TodoEvent, TodoState> implements TodoBloc {}

void main() {
  testWidgets('xəta state-ində SnackBar göstərilir', (tester) async {
    final bloc = MockTodoBloc();

    // whenListen: state ardıcıllığını stub kimi verir və `state`-i sinxronlaşdırır.
    whenListen(
      bloc,
      Stream.fromIterable(const [
        TodoState(status: TodoStatus.loading),
        TodoState(status: TodoStatus.failure, error: 'Şəbəkə xətası'),
      ]),
      initialState: const TodoState(),
    );

    await tester.pumpWidget(
      MaterialApp(
        home: BlocProvider<TodoBloc>.value(
          value: bloc,
          child: const TodoPage(),
        ),
      ),
    );

    await tester.pump(); // loading
    await tester.pump(); // failure → listener işə düşür

    expect(find.text('Şəbəkə xətası'), findsOneWidget);
  });
}

A widget test: isolating the UI with `MockBloc` and `whenListen`.

What is assertedParameter / tool
The state sequence (loading → success)`expect`
Starting from a given state`seed`
Debounce/throttle behaviour`wait`
Ignoring the first n states`skip`
That a repository/service was called`verify`
Thrown errors`errors`
Isolating the UI from the bloc entirely`MockBloc` plus `whenListen`

Asserting the state sequence with expect is powerful but can be brittle: adding a field to the state class breaks every test. The practical approach is to assert the critical fields with matchers (isA<TodoState>().having((s) => s.status, 'status', TodoStatus.success)) and to require full equality only for small, stable states.

📚 Sources and documentation