如何在应用中对带有随机参数的方法进行存根?

人工智能 2026-07-07

如何对controller.sink.add() 的调用进行桩化(存根),以便它接收一个随机输入值?

领域类正在像下面这样调用该方法。

class DomainClass extends ChangeNotifier {
  late final DiVariables diVariables;
  num answer = 0;
  StreamSink<num> get _input => diVariables.streamController.sink;
  Stream<num> get outputStream => diVariables.streamController.stream;
  DomainClass({required DIVariables diVariablesParam}) {
    this.diVariables = diVariablesParam;
    calculateOutput(diVariables: diVaririables);
  }
  int testableRandomInt(num maxNumber) => Random().nextInt(maxNumber.toInt());
  Future<void> calculateOutput({required DIVariables diVariables}) {
    //calculation of variable to add to stream
    var temp = testableRandomInt(diVariables.streamsProcessorList[0]);
    _input.add(temp); // add method is called
  }
}
class DIVariables {
  late final List streamsProcessorList = List.filled(2, null);
  final _streamController = StreamController<num>.broadcast();
  StreamController<num> get streamController => _streamController;
  DIVariables({
    required SelectedOperation selectedOperation,
    required int stopCount,
    required String value,
  }) {
    this.selectedOperation = selectedOperation;
    streamsProcessorList[0] = stopCount;
    streamsProcessorList[1] = int.parse(value) + 1;
  }
}

现在,这个类在小部件中通过StreamBuilder显示流中的值。

class _NumberScreenState extends State<NumberScreen> {
  late DomainClass domainClass;

  @override
  Widget build(BuildContext context) {
    domainClass = DomainClass(
    diVariablesParameter: getIt<DIVariables>());
    return Scaffold(
      body: StreamBuilder<num>(
        stream: getIt<DIVariables>().outputStream,
        builder: (context, snapshot) {
          if((snapshot.connectionState==ConnectionState.active||
              snapshot.connectionState==ConnectionState.done)&&
              snapshot.hasData) {
            return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  snapshot.data.toString(),
                  style: const TextStyle(
                    fontSize: 10,
                    fontWeight: FontWeight.w800,
                  ),
                ),
                ElevatedButton(
                  key: const Key('buttonExit'),
                  child: const Text('Exit'),
                  onPressed: () {
                    domainClass.elevatedButtonPress(
                            ()=> Navigator.pop(context,domainClass.answer)
                        , snapshot);
                  },
                ),
              ],
            ),
          );
          } else {
            return const Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }
}

我有一个小部件测试文件,其中DIVariables类已被模拟,但我无法用测试文件中定义的控制器来替换流,以便让它产生值,而不是使用真实实现。

我正在尝试通过以下AI建议的控制器模拟来测试小部件:

testWidgets('StreamBuilder displays data', (WidgetTester tester) async {
  final controller = StreamController<String>();

  await tester.pumpWidget(
    MaterialApp(
      home: StreamBuilder<String>(
        stream: controller.stream,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return Text(snapshot.data!);
          }
          return const Text('Loading');
        },
      ),
    ),
  );

  // Initial state
  expect(find.text('Loading'), findsOneWidget);

  // Emit data
  controller.add('Hello');
  await tester.pump(); // Trigger rebuild

  // Verify updated state
  expect(find.text('Hello'), findsOneWidget);

  controller.close();
});

此外,我还希望获得帮助,在满足条件时测试CircularProgressIndicator小部件在屏幕上出现的情况,因为在小部件测试文件中无法访问snapshot。

解决方案

Random().nextInt() 不是你这里应该进行桩化的对象。你的testableRandomInt() 方法已经为在需要时覆盖或模拟随机值提供了一个切点。

更大的问题在于DomainClass会从DIVariables创建并使用StreamController,这意味着你的小部件测试无法控制会发出什么。

与其去模拟sink.add(),不如注入依赖。

class DomainClass {
  DomainClass({
    required this.input,
    required this.output,
    required this.random,
  });

  final StreamSink<num> input;
  final Stream<num> output;
  final Random random;

  void calculateOutput(int max) {
    input.add(random.nextInt(max));
  }
}

那么你的测试就变得直接了当:

final controller = StreamController<num>();

final domain = DomainClass(
  input: controller.sink,
  output: controller.stream,
  random: FakeRandom(), // returns a deterministic value
);

或者简单地由你自己发出值:

controller.add(42);
await tester.pump();

expect(find.text('42'), findsOneWidget);

要测试CircularProgressIndicator,请在第一次pump之前不要发出任何数据:

await tester.pumpWidget(MyWidget());

expect(find.byType(CircularProgressIndicator), findsOneWidget);

controller.add(42);
await tester.pump();

expect(find.byType(CircularProgressIndicator), findsNothing);
expect(find.text('42'), findsOneWidget);

如果你在使用get_it,请在泵送小部件之前注册测试用的DIVariables(包含你的测试StreamController),而不是尝试去模拟StreamSink.add()。一般而言,测试一个真实的StreamController比模拟StreamSink API要容易得多。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章