Sparround

bloc_test: blocTest, MockBloc, whenListen

package:bloc_test bloc/cubit testini deklarativ formaya salır. Əsas funksiya blocTest və onun parametrləri:

  • `build` — test olunan bloc-u qurub qaytarır.
  • `setUp` — bloc yaradılmadan əvvəl asılılıqları hazırlamaq.
  • `seed` — bloc-a ilkin state "əkmək" (məsələn dolu siyahıdan başlamaq).
  • `act` — bloc ilə qarşılıqlı əlaqə: event əlavə etmək, cubit metodu çağırmaq.
  • `wait` — debounce kimi async əməliyyatlar üçün gözləmə müddəti.
  • `skip` — gözləntilərdən əvvəl buraxılacaq state-lərin sayı (default 0).
  • `expect` — gözlənilən state ardıcıllığı (matcher).
  • `verify`expect-dən sonra əlavə yoxlamalar (məsələn repository bir dəfə çağırıldı).
  • `errors` — gözlənilən xətalar.
  • `tearDown` — testdən sonra təmizləmə.

Əlavə alətlər: `MockBloc`/`MockCubit` — bloc-un stub versiyasını yaratmaq üçün; `whenListen` — bloc-un listen metoduna hazır cavab qurur və state xassəsini yayımlanan dəyərlərlə sinxronlaşdırır.

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: state ardıcıllığı və `verify` ilə əlavə yoxlama.

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);
  });
}

Widget testi: `MockBloc` və `whenListen` ilə UI-ı izolyasiya etmək.

Nə yoxlanılırParametr / alət
State ardıcıllığı (loading → success)`expect`
Müəyyən state-dən başlamaq`seed`
Debounce/throttle davranışı`wait`
İlk n state-i nəzərə almamaq`skip`
Repository/servisin çağırılması`verify`
Atılan xətalar`errors`
UI-ı bloc-dan tam ayırmaq`MockBloc` + `whenListen`

expect ilə state ardıcıllığını yoxlamaq güclüdür, lakin kövrək ola bilər: state sinfinə yeni sahə əlavə edildikdə bütün testlər sınır. Praktik yanaşma — kritik sahələri matcher ilə yoxlamaq (isA<TodoState>().having((s) => s.status, 'status', TodoStatus.success)), tam bərabərliyi isə yalnız kiçik və sabit state-lərdə tələb etmək.

📚 Mənbələr və sənədlər