简体   繁体   English

如何使用模拟 cubit 实现小部件测试?

[英]How to implement widget tests by using mock cubit?

I tried to follow the answer to this question , but I was not able to make it work.我试图按照这个问题的答案,但我无法让它工作。

I reproduced my issue on the counter app, changing it as follow.我在柜台应用程序上重现了我的问题,将其更改如下。

main.dart

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(
          create: (BuildContext ctx) => DummyCubit(),
        ),
      ],
      child: MaterialApp(
      ...
}

class _MyHomePageState extends State<MyHomePage> {
  ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      ...
      body: Center(
        child: CounterViewer(counter: _counter),
      ),
      ...
    );
  }
}

class CounterViewer extends StatelessWidget {
  const CounterViewer({required this.counter, Key? key}) : super(key: key);

  final int counter;

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<DummyCubit, AState>(
      builder: (ctx, state) => (state is! StateLoaded)
          ? const CircularProgressIndicator()
          : Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  '$counter',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ],
            ),
    );
  }
}

dummy_cubit.dart

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

class DummyCubit extends Cubit<AState> {
  DummyCubit() : super(const InitState());

  Future<void> executeLogic() async {
    emit(const StateLoading());
    // do some logic
    emit(StateLoaded('some data'));
  }
}

@immutable
abstract class AState {
  const AState();
}

class InitState extends AState {
  const InitState();
}

class StateLoading extends AState {
  const StateLoading();
}

class StateLoaded extends AState {
  const StateLoaded(this.data);

  final String data;

  @override
  String toString() => data.toString();

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      (other is StateLoaded &&
          runtimeType == other.runtimeType &&
          data == other.data);

  @override
  int get hashCode => data.hashCode;
}

widget_test.dart

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

import 'package:counter/dummy_cubit.dart';
import 'package:counter/main.dart';

class MockDummyCubit extends MockCubit<AState> implements DummyCubit {}

class AStateFake extends Fake implements AState {}

final dummyCubit = MockDummyCubit();

Widget get counter => MultiBlocProvider(
      providers: [
        BlocProvider<DummyCubit>(
          create: (BuildContext ctx) => dummyCubit,
        ),
      ],
      child: const MaterialApp(
        home: CounterViewer(counter: 1),
      ),
    );

void main() {
  setUpAll(() {
    mocktail.registerFallbackValue(AStateFake());
  });

  group('Counter viewer', () {
    mocktail.when(() => dummyCubit.state).thenReturn(InitState());
    testWidgets('should build', (WidgetTester tester) async {
      await tester.pumpWidget(counter);
    });
  });
}

When running the test, I get this error:运行测试时,我收到此错误:

The following StateError was thrown running a test:
Bad state: No method stub was called from within `when()`. Was a real method called, or perhaps an
extension method?

And removing the mocktail.when line, I get this error:并删除mocktail.when行,我收到此错误:

The following _TypeError was thrown building CounterViewer:
type 'Null' is not a subtype of type 'AState'

How do I solve this issue?我该如何解决这个问题?

How do I control which state is emitted by my DummyCubit?如何控制 DummyCubit 发出的状态?

After reading this , I found the solution阅读本文后,我找到了解决方案

class MockDummyCubit extends MockCubit<AState> implements DummyCubit {}

class AStateFake extends Fake implements AState {}

void main() {
  late MockDummyCubit dummyCubit;

  setUpAll(() {
    mocktail.registerFallbackValue(AStateFake());
  });

  setUp(() {
    dummyCubit = MockDummyCubit();
    mocktail.when(() => dummyCubit.state).thenReturn(const InitState());
  });

  group('Counter viewer', () {
    testWidgets('should build', (WidgetTester tester) async {
      await tester.pumpWidget(getCounter(dummyCubit));
    });
  });
}

Widget getCounter(MockDummyCubit dummyCubit) => MultiBlocProvider(
      providers: [
        BlocProvider<DummyCubit>(
          create: (BuildContext ctx) => dummyCubit,
        ),
      ],
      child: const MaterialApp(
        home: CounterViewer(counter: 1),
      ),
    );

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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