AsyncRedux测试高级等待方法Skill asyncredux-testing-wait-methods

这个技能专注于在 AsyncRedux 框架中使用高级等待方法进行复杂测试场景。它涵盖了 waitCondition、waitAllActions、waitActionType、waitAllActionTypes、waitAnyActionTypeFinishes 等函数,以及 completeImmediately 参数,帮助开发者精确控制异步测试流程,确保代码的准确性和可靠性。关键词:AsyncRedux、测试、等待方法、异步编程、Dart、Flutter、软件测试、移动开发。

测试 0 次安装 4 次浏览 更新于 3/19/2026

名称: asyncredux-testing-wait-methods 描述: 使用高级等待方法处理复杂测试场景。覆盖 waitCondition()waitAllActions()waitActionType()waitAllActionTypes()waitAnyActionTypeFinishes() 以及 completeImmediately 参数。

测试高级等待方法

在 AsyncRedux 中测试复杂异步场景时,基本的 dispatchAndWait() 可能不足够。存储提供了几种高级等待方法,用于精细控制测试何时进行。

等待方法概述

方法 目的
waitCondition() 等待直到状态满足条件
waitAllActions() 等待特定动作完成,或直到没有动作在进行中
waitActionType() 等待直到给定类型的动作不在进行中
waitAllActionTypes() 等待直到给定类型的所有动作不在进行中
waitAnyActionTypeFinishes() 等待直到给定类型的任何动作完成
waitActionCondition() 低级:等待直到进行中的动作满足自定义条件

waitCondition()

等待直到状态满足给定条件。返回触发状态变化的动作。

Future<ReduxAction<St>?> waitCondition(
  bool Function(St) condition, {
  bool completeImmediately = true,  // 注意:这里默认是 TRUE
  int? timeoutMillis,
})

基本用法

test('waitCondition 等待状态匹配', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  // 分发一个将改变状态的异步动作
  store.dispatch(IncrementActionAsync());

  // 等待直到计数变为 2
  var action = await store.waitCondition((state) => state.count == 2);

  expect(store.state.count, 2);
  expect(action, isA<IncrementActionAsync>());
});

条件已为真

默认情况下,如果条件已为真,未来立即完成:

test('当条件已为真时立即完成', () async {
  var store = Store<AppState>(initialState: AppState(count: 5));

  // 条件已为真 - 立即完成
  await store.waitCondition((state) => state.count == 5);

  expect(store.state.count, 5);
});

使用 completeImmediately: false

要求条件必须变为真(而不是已经为真):

test('当条件已为真且 completeImmediately: false 时抛出异常', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  // 这将抛出因为条件已为真
  expect(
    () => store.waitCondition(
      (state) => state.count == 1,
      completeImmediately: false,
    ),
    throwsA(isA<StoreException>()),
  );
});

waitAllActions()

等待特定动作完成,或等待直到没有动作在进行中(当传递空列表或 null 时)。

Future<void> waitAllActions(
  List<ReduxAction<St>>? actions, {
  bool completeImmediately = false,  // 注意:这里默认是 FALSE
  int? timeoutMillis,
})

等待所有动作完成

test('waitAllActions 等待所有分发的动作', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  var action1 = DelayedIncrementAction(10, delayMillis: 50);
  var action2 = DelayedIncrementAction(100, delayMillis: 100);
  var action3 = DelayedIncrementAction(1000, delayMillis: 20);

  // 并行分发动作
  store.dispatch(action1);
  store.dispatch(action2);
  store.dispatch(action3);

  expect(store.state.count, 1); // 尚未改变

  // 等待所有三个动作完成
  await store.waitAllActions([action1, action2, action3]);

  expect(store.state.count, 1 + 10 + 100 + 1000);
});

等待直到没有动作在进行中

传递空列表或 null 以等待直到没有动作在运行:

test('waitAllActions 使用空列表等待所有完成', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  store.dispatch(DelayedAction(10, delayMillis: 50));
  store.dispatch(DelayedAction(100, delayMillis: 100));
  store.dispatch(DelayedAction(1000, delayMillis: 20));

  expect(store.state.count, 1);

  // 等待直到所有动作完成(没有动作在进行中)
  await store.waitAllActions([]);

  expect(store.state.count, 1 + 10 + 100 + 1000);
});

选择性等待

只等待某些动作完成,忽略其他:

test('只等待特定动作', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  var action50 = DelayedAction(10, delayMillis: 50);
  var action100 = AnotherDelayedAction(100, delayMillis: 100);
  var action200 = SlowAction(100000, delayMillis: 200); // 非常慢
  var action10 = DelayedAction(1000, delayMillis: 10);

  store.dispatch(action50);
  store.dispatch(action100);
  store.dispatch(action200); // 我们不等这一个
  store.dispatch(action10);

  // 只等待快速动作
  await store.waitAllActions([action50, action100, action10]);

  // 慢动作尚未完成
  expect(store.state.count, 1 + 10 + 100 + 1000);
});

waitActionType()

等待直到给定类型的动作不在进行中。返回完成的动作(或如果没有动作在进行中则返回 null)。

Future<ReduxAction<St>?> waitActionType(
  Type actionType, {
  bool completeImmediately = false,
  int? timeoutMillis,
})

基本用法

test('waitActionType 等待动作类型完成', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  store.dispatch(DelayedAction(1000, delayMillis: 10));

  expect(store.state.count, 1);

  // 等待任何 DelayedAction 完成
  var action = await store.waitActionType(DelayedAction);

  expect(store.state.count, 1001);
  expect(action, isA<DelayedAction>());
});

检查动作状态

test('可以检查完成动作的状态', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  store.dispatch(ActionThatMayFail());

  var action = await store.waitActionType(ActionThatMayFail);

  expect(action?.status.isCompletedOk, isTrue);
  // 或检查错误:
  // expect(action?.status.originalError, isA<UserException>());
});

顺序等待多个类型

test('等待多个动作类型', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  store.dispatch(AnotherDelayedAction(123, delayMillis: 100));
  store.dispatch(DelayedAction(1000, delayMillis: 10));

  expect(store.state.count, 1);

  // DelayedAction 先完成(10ms)
  await store.waitActionType(DelayedAction);
  expect(store.state.count, 1001);

  // AnotherDelayedAction 后完成(100ms)
  await store.waitActionType(AnotherDelayedAction);
  expect(store.state.count, 1124);
});

waitAllActionTypes()

等待直到给定类型的所有动作不在进行中。

Future<void> waitAllActionTypes(
  List<Type> actionTypes, {
  bool completeImmediately = false,
  int? timeoutMillis,
})

基本用法

test('waitAllActionTypes 等待所有类型', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  store.dispatch(DelayedAction(10, delayMillis: 50));
  store.dispatch(AnotherDelayedAction(100, delayMillis: 100));
  store.dispatch(SlowAction(100000, delayMillis: 200));
  store.dispatch(DelayedAction(1000, delayMillis: 10));

  expect(store.state.count, 1);

  // 只等待 DelayedAction 和 AnotherDelayedAction 类型
  await store.waitAllActionTypes([DelayedAction, AnotherDelayedAction]);

  // SlowAction 尚未完成(200ms),但我们没等它
  expect(store.state.count, 1 + 10 + 100 + 1000);
});

waitAnyActionTypeFinishes()

重要: 此方法与其他方法不同。它等待直到给定类型的任何动作完成分发,即使这些动作在方法调用时不在进行中。

Future<ReduxAction<St>> waitAnyActionTypeFinishes(
  List<Type> actionTypes, {
  int? timeoutMillis,
})

用例:等待嵌套动作

这在动作内部分发其他动作时很有用,您想等待其中一个嵌套动作完成:

test('waitAnyActionTypeFinishes 等待嵌套动作', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  // StartAction 内部分发 DelayedAction
  store.dispatch(StartAction());

  // 等待 DelayedAction 完成(即使它尚未分发)
  var action = await store.waitAnyActionTypeFinishes([DelayedAction]);

  expect(action, isA<DelayedAction>());
  expect(action.status.isCompletedOk, isTrue);
});

多个类型 - 第一个完成的

test('返回第一个完成的动作类型', () async {
  var store = Store<AppState>(initialState: AppState());

  store.dispatch(ProcessStocksAction()); // 分发 BuyAction 或 SellAction

  // 等待 BuyAction 或 SellAction 完成
  var action = await store.waitAnyActionTypeFinishes([BuyAction, SellAction]);

  expect(action.runtimeType, anyOf(equals(BuyAction), equals(SellAction)));
});

waitActionCondition()

低级方法,等待直到进行中的动作集满足自定义条件。这是其他等待方法内部使用的方法。

Future<(Set<ReduxAction<St>>, ReduxAction<St>?)> waitActionCondition(
  bool Function(Set<ReduxAction<St>> actions, ReduxAction<St>? triggerAction) condition, {
  bool completeImmediately = false,
  String completedErrorMessage = "等待的动作条件已为真",
  int? timeoutMillis,
})

示例:自定义条件

test('waitActionCondition 使用自定义条件', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  // 等待直到没有动作在进行中
  await store.waitActionCondition(
    (actions, triggerAction) => actions.isEmpty,
    completeImmediately: true,
  );
});

completeImmediately 参数

此参数控制当方法调用时条件已满足时的行为:

方法 默认 true false
waitCondition true 立即完成 抛出 StoreException
waitAllActions false 立即完成 抛出 StoreException
waitActionType false 立即完成,返回 null 抛出 StoreException
waitAllActionTypes false 立即完成 抛出 StoreException
waitActionCondition false 立即完成 抛出 StoreException

注意: waitCondition 默认是 true,因为它常用于检查“状态是否就绪?”,在这种情况下,如果已经就绪,您想继续。其他方法默认是 false,因为它们通常用于等待应该在进行的动作。

test('completeImmediately 行为', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  // waitCondition:completeImmediately 默认是 TRUE
  await store.waitCondition((state) => state.count == 1); // OK,完成

  // waitAllActions:completeImmediately 默认是 FALSE
  expect(
    () => store.waitAllActions([]), // 没有动作在进行中
    throwsA(isA<StoreException>()),
  );

  // 使用 completeImmediately: true 来允许
  await store.waitAllActions([], completeImmediately: true); // OK
});

超时配置

所有等待方法都支持 timeoutMillis 参数。默认超时是 10 分钟。

test('waitCondition 带超时', () async {
  var store = Store<AppState>(initialState: AppState(count: 1));

  // 这个条件永远不会为真,所以会超时
  expect(
    () => store.waitCondition(
      (state) => state.count == 999,
      timeoutMillis: 10, // 10ms 超时
    ),
    throwsA(isA<TimeoutException>()),
  );
});

全局超时配置

修改 Store.defaultTimeoutMillis 以更改所有等待方法的默认值:

void main() {
  // 设置全局默认超时为 30 秒
  Store.defaultTimeoutMillis = 30 * 1000;

  // 要完全禁用超时,使用 -1
  Store.defaultTimeoutMillis = -1;
}

完整测试示例

import 'dart:async';
import 'package:async_redux/async_redux.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  group('等待方法', () {
    test('waitCondition 等待状态变化', () async {
      var store = Store<State>(initialState: State(1));

      // 分发异步动作
      store.dispatch(IncrementActionAsync());

      // 等待状态改变
      await store.waitCondition((state) => state.count == 2);

      expect(store.state.count, 2);
    });

    test('waitAllActions 等待所有动作', () async {
      var store = Store<State>(initialState: State(1));

      store.dispatch(DelayedAction(10, delayMillis: 50));
      store.dispatch(DelayedAction(100, delayMillis: 100));
      store.dispatch(DelayedAction(1000, delayMillis: 20));

      await store.waitAllActions([]);

      expect(store.state.count, 1111);
    });

    test('waitActionType 等待特定类型', () async {
      var store = Store<State>(initialState: State(1));

      store.dispatch(DelayedAction(1000, delayMillis: 10));

      var action = await store.waitActionType(DelayedAction);

      expect(store.state.count, 1001);
      expect(action?.status.isCompletedOk, isTrue);
    });

    test('waitAllActionTypes 等待多个类型', () async {
      var store = Store<State>(initialState: State(1));

      store.dispatch(DelayedAction(10, delayMillis: 50));
      store.dispatch(AnotherAction(100, delayMillis: 100));

      await store.waitAllActionTypes([DelayedAction, AnotherAction]);

      expect(store.state.count, 111);
    });

    test('waitAnyActionTypeFinishes 等待第一个完成', () async {
      var store = Store<State>(initialState: State(1));

      store.dispatch(DelayedAction(1, delayMillis: 10));

      var action = await store.waitAnyActionTypeFinishes([DelayedAction]);

      expect(action, isA<DelayedAction>());
      expect(action.status.isCompletedOk, isTrue);
    });
  });
}

// 测试状态和动作
class State {
  final int count;
  State(this.count);
}

class IncrementActionAsync extends ReduxAction<State> {
  @override
  Future<State> reduce() async {
    await Future.delayed(Duration(milliseconds: 10));
    return State(state.count + 1);
  }
}

class DelayedAction extends ReduxAction<State> {
  final int increment;
  final int delayMillis;

  DelayedAction(this.increment, {required this.delayMillis});

  @override
  Future<State> reduce() async {
    await Future.delayed(Duration(milliseconds: delayMillis));
    return State(state.count + increment);
  }
}

class AnotherAction extends DelayedAction {
  AnotherAction(int increment, {required int delayMillis})
      : super(increment, delayMillis: delayMillis);
}

参考

文档中的 URL: