You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

原因分析

  1. Mock的setter无效:你定义的physicalKey和logicalKey setter只是直接返回传入的key,没有实际给父类的对应属性赋值。父类RawKeyEvent的这两个属性是只读的,你的setter并没有真正修改对象的内部状态,导致isKeyPressed无法识别设置的按键。
  2. 父类判断逻辑不匹配: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类的麻烦:

  1. 添加依赖到pubspec.yaml的dev_dependencies:
dev_dependencies:
  mockito: ^5.4.0
  build_runner: ^2.4.0
  1. 生成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
  1. 测试中使用:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 16:03:37