使用BlocTest模拟按键事件时isKeyPressed返回false的问题
问题
测试Cubit的按键处理函数时,用blocTest模拟按键事件,没法调用simulateKeyDownEvent(因为blocTest只关注Bloc/Cubit而非组件),于是Mock了RawKeyDownEvent,但即使模拟了正确按键,isKeyPressed仍返回false。
Cubit按键处理代码
void handleKeyPress(RawKeyEvent event) { if (event.isKeyPressed(LogicalKeyboardKey.keyU)) { ... } ... }
测试及Mock事件代码
测试代码:
blocTest<Cubit, State>('Handle key press, increase layer', build: () => cubit, // defined in setup act: (cubit) async { RawKeyEvent mockEnterKey = const RawKeyDownEventMock( data: RawKeyEventDataWindows(keyCode: 13, scanCode: 28, characterCodePoint: 0, modifiers: 0), character: 'enter') ..physicalKey = PhysicalKeyboardKey.enter ..logicalKey = LogicalKeyboardKey.enter; cubit.handleKeyPress(mockEnterKey); }, expect: () => [isA<State>().having((state) => state.currentLayer, 'curent layer', initState + 1)]);
Mock事件定义:
class RawKeyDownEventMock extends RawKeyEvent { const RawKeyDownEventMock({required data, String? character, bool repeat = false}) : super(data: data, character: character, repeat: repeat); set physicalKey(PhysicalKeyboardKey key) => key; set logicalKey(LogicalKeyboardKey key) => key; @override bool isKeyPressed(LogicalKeyboardKey key) { // Showing as false in tests even when simulating key // logical key id - name - physical key id // 4294967309 - enter - 458792 // 117 - U - 458776 // 97 - A - 458756 // if (key.keyId == 4294967309 || key.keyId == 117 || key.keyId == 97) return true; return super.isKeyPressed(key); } }
原因分析
- Mock的setter无效:你定义的
physicalKey和logicalKeysetter只是直接返回传入的key,没有实际给父类的对应属性赋值。父类RawKeyEvent的这两个属性是只读的,你的setter并没有真正修改对象的内部状态,导致isKeyPressed无法识别设置的按键。 - 父类判断逻辑不匹配:
RawKeyEvent的isKeyPressed方法依赖内部的RawKeyEventData数据,你传入的RawKeyEventDataWindows参数和手动设置的logicalKey不关联,父类方法无法匹配到对应的按键,所以返回false。
解决方法
方法一:直接重写isKeyPressed方法
既然是Mock类,直接在重写方法里判断目标按键,返回true即可,不用依赖父类逻辑:
class RawKeyDownEventMock extends RawKeyEvent { final LogicalKeyboardKey _targetKey; const RawKeyDownEventMock({ required data, String? character, bool repeat = false, required LogicalKeyboardKey targetKey, }) : _targetKey = targetKey, super(data: data, character: character, repeat: repeat); @override bool isKeyPressed(LogicalKeyboardKey key) { return key == _targetKey; } }
测试中使用方式:
RawKeyEvent mockEnterKey = RawKeyDownEventMock( data: RawKeyEventDataWindows(keyCode: 13, scanCode: 28, characterCodePoint: 0, modifiers: 0), character: 'enter', targetKey: LogicalKeyboardKey.enter, );
方法二:用mockito库Mock事件
用mockito可以更灵活Stub方法行为,避免手动写Mock类的麻烦:
- 添加依赖到
pubspec.yaml的dev_dependencies:
dev_dependencies: mockito: ^5.4.0 build_runner: ^2.4.0
- 生成Mock类:
创建test/mocks.dart文件:
import 'package:flutter/services.dart'; import 'package:mockito/mockito.dart'; class MockRawKeyDownEvent extends Mock implements RawKeyDownEvent {}
运行命令生成mock:
flutter pub run build_runner build
- 测试中使用:
blocTest<Cubit, State>('Handle key press, increase layer', build: () => cubit, act: (cubit) async { final mockEvent = MockRawKeyDownEvent(); when(mockEvent.isKeyPressed(LogicalKeyboardKey.enter)).thenReturn(true); cubit.handleKeyPress(mockEvent); }, expect: () => [isA<State>().having((state) => state.currentLayer, 'current layer', initState + 1)]);
方法三:匹配RawKeyEventData与逻辑按键
如果想保留父类逻辑,需要确保RawKeyEventData参数能正确映射到目标logicalKey。比如LogicalKeyboardKey.keyU对应的Windows keyCode是85,传入对应参数后,父类isKeyPressed就能正确识别:
RawKeyEvent mockUKey = const RawKeyDownEventMock( data: RawKeyEventDataWindows(keyCode: 85, scanCode: 22, characterCodePoint: 117, modifiers: 0), character: 'u', );
这种方式需要对应不同平台的keyCode,通用性较差,不如前两种方法方便。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

