简体   繁体   中英

Flutter bloc: how to test bloc constructor while mocking event calls?

I would like to test if a timer initialized by my constructor effectively calls a specific bloc event.

import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';

class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
  ExampleBloc() : super(ExampleInitial()) {
    on<ExampleEvent>((event, emit) {});
    periodicTimer = Timer.periodic(Duration(minutes: 1), (timer) async {
      add(ExampleEvent());
    });
  }
  late Timer periodicTimer;

  @override
  Future<void> close() {
    periodicTimer.cancel();
    return super.close();
  }
}

@immutable
class ExampleEvent {}

@immutable
abstract class ExampleState {}

class ExampleInitial extends ExampleState {}

To test it, I thought about using a widget test because it would be possible to tester.pump() the duration from the timer.

import 'dart:async';

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:indaband/example_bloc.dart';
import 'package:mocktail/mocktail.dart';

class ExampleBlocMock extends MockBloc<ExampleEvent, ExampleState>
    implements ExampleBloc {}

void registerFallbackValues() {
  registerFallbackValue<ExampleState>(ExampleInitial());
  registerFallbackValue<ExampleEvent>(ExampleEvent());
}

void main() {
  setUpAll(() {
    registerFallbackValues();
  });
  group(
    'Test',
    () {
      final bloc = ExampleBlocMock();
      final blocStateController = StreamController<ExampleState>.broadcast();
      when(() => bloc.stream).thenAnswer((_) => blocStateController.stream);

      testWidgets('test periodic timer', (WidgetTester tester) async {
        final bloc = ExampleBloc();
        final widget = BlocProvider(
          create: (context) => bloc,
          child: Container(),
        );
        await tester.pumpWidget(widget);
        await tester.pump(Duration(minutes: 1));
        verify(() => bloc.add(ExampleEvent())).called(1);
        await tester.pump(Duration(minutes: 1));
        verify(() => bloc.add(ExampleEvent())).called(2);
        await tester.pump(Duration(minutes: 1));
        verify(() => bloc.add(ExampleEvent())).called(3);
      });
    },
  );
}

I don't know if it's possible to mock only the .add(...) call, in order to make the timer available.

There's a few things wrong here:

  1. Your creating a mock bloc in your test, and it's not being used or verified at all given you're creating a 'real' bloc with the same name.

  2. Your ExampleEvent isn't emitting a new state on each timer tick, so there's nothing to test or capture.

  3. Testing the bloc itself is a unit test, not a widget test. The bloc_test package provides a convenient set of utilities to unit test blocs, read the docs.

  4. Bloc events/states must implement equality overrides to ensure a new state is emitted after each event, refer to the bloc docs and in-depth tutorials as to how the bloc package works.

Once the above is addressed, you can add test cases around verifying the emitted state is what you expect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM