flutter - 如何在 Flutter 中模拟一个块,并发出状态以响应来自被测小部件的事件
问题描述
我正在尝试测试一个使用 bloc 的小部件。我希望能够从我的模拟块中发出状态,以响应被测小部件触发的事件。我尝试了很多方法都没有成功。我不确定我是否犯了一些简单的错误,或者我是否完全错误地解决了这个问题。
这是一个简化的项目,它演示了我的问题。(完整的代码可以在https://github.com/andrewdixon1000/flutter_bloc_mocking_issue.git找到)
非常简单的集团
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
class MyBloc extends Bloc<MyEvent, MyState> {
MyBloc() : super(FirstState());
@override
Stream<MyState> mapEventToState(
MyEvent event,
) async* {
if (event is TriggerStateChange) {
yield SecondState();
}
}
}
@immutable
abstract class MyEvent {}
class TriggerStateChange extends MyEvent {}
@immutable
abstract class MyState {}
class FirstState extends MyState {}
class SecondState extends MyState {}
我的小部件正在测试中
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/my_bloc.dart';
import 'injection_container.dart';
class FirstPage extends StatefulWidget {
const FirstPage({Key? key}) : super(key: key);
@override
_FirsPageState createState() => _FirsPageState();
}
class _FirsPageState extends State<FirstPage> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => serviceLocator<MyBloc>(),
child: Scaffold(
appBar: AppBar(title: Text("Page 1")),
body: Container(
child: BlocConsumer<MyBloc, MyState>(
listener: (context, state) {
if (state is SecondState) {
Navigator.pushNamed(context, "SECONDPAGE");
}
},
builder: (context, state) {
if (state is FirstState) {
return Column(
children: [
Text("State is FirstState"),
ElevatedButton(
onPressed: () {
BlocProvider.of<MyBloc>(context).add(TriggerStateChange());
},
child: Text("Change state")),
],
);
} else {
return Text("some other state");
}
},
),
),
),
);
}
}
我的小部件测试 这是我苦苦挣扎的地方。我正在做的是加载小部件,然后点击按钮。这会导致小部件将事件添加到块中。我想要做的是让我的模拟块发出一个状态来响应这个,这样小部件的 BlocConsumer 的侦听器将看到状态更改导航。正如您从代码中的评论中看到的那样,我尝试了一些没有运气的事情。目前我没有尝试过导致监听器看到状态变化。
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart' as mocktail;
import 'package:get_it/get_it.dart';
import 'package:test_bloc_issue/bloc/my_bloc.dart';
import 'package:test_bloc_issue/first_page.dart';
class MockMyBloc extends MockBloc<MyEvent, MyState> implements MyBloc {}
class FakeMyState extends Fake implements MyState {}
class FakeMyEvent extends Fake implements MyEvent {}
void main() {
MockMyBloc mockMyBloc;
mocktail.registerFallbackValue<MyState>(FakeMyState());
mocktail.registerFallbackValue<MyEvent>(FakeMyEvent());
mockMyBloc = MockMyBloc();
var nextScreenPlaceHolder = Container();
setUpAll(() async {
final di = GetIt.instance;
di.registerFactory<MyBloc>(() => mockMyBloc);
});
_loadScreen(WidgetTester tester) async {
mocktail.when(() => mockMyBloc.state).thenReturn(FirstState());
await tester.pumpWidget(
MaterialApp(
home: FirstPage(),
routes: <String, WidgetBuilder> {
'SECONDPAGE': (context) => nextScreenPlaceHolder
}
)
);
}
testWidgets('test', (WidgetTester tester) async {
await _loadScreen(tester);
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
// What do I need to do here to mock the state change that would
// happen in the real bloc when a TriggerStateChange event is received,
// such that the listener in my BlocConsumer will see it?
// if tried:
// whenListen(mockMyBloc, Stream<MyState>.fromIterable([SecondState()]));
// and
// mocktail.when(() => mockMyBloc.state).thenReturn(SecondState());
await tester.pumpAndSettle();
expect(find.byWidget(nextScreenPlaceHolder), findsOneWidget);
});
}
解决方案
我看了看并用我的建议打开了一个拉取请求。我强烈建议您根据通知和反应来考虑您的测试。在这种情况下,我建议进行一项测试,以验证当点击按钮时,是否将正确的事件添加到 bloc(通知 bloc)。然后我建议进行单独的测试,以确保当状态从 FirstState 更改为 SecondState 时呈现正确的页面(UI 对状态更改做出适当的反应)。将来,我强烈建议您查看示例应用程序,因为它们中的大多数都经过了全面测试。
推荐阅读
- python - NotImplementedError:尚不支持位打包
- javascript - 如何在 vue js 表中显示 Json 中的第一项?
- php - 如何解决 Laravel 不生成 CSRF 令牌
- c++ - 有没有保证 sizeof(which) > 1 的简单类型?
- solr - 如何备份和恢复 Solr 集群数据?
- c# - ASP.NET Core 5.0 JWT 身份验证抛出 401 代码
- mysql - POST API设计问卷
- python - 从函数获取“未定义”输出
- angular - Angular JS - 猫头鹰轮播的占位符动画
- python - 比较缺少一些条目的两个数据帧