ReactiveX / rxdart

The Reactive Extensions for Dart
http://reactivex.io
Apache License 2.0
3.36k stars 271 forks source link

BehaviorSubject doesn't provide value via 'first' in test environment #702

Open agavrilko opened 1 year ago

agavrilko commented 1 year ago

The dartpad example: https://dartpad.dev/?id=02878d4135fdf329059f95fb9f7a72c4

Description

The issue only appears in widget test environment ('testWidgets'). If widget utilize the 'stream.first' within itself or bloc, not really matters, and stream is BehaviorSubject, the Future provided by 'first' never finishes.

Example

The widget in the example below just shows text 'Value = $number' with the number from assigned stream.

In the real environment the last added value to the stream is shown on the screen. But in the test we see 'Value = -1' because timeout fires. Without timeout widget never receives any value.

void main() {
  testWidgets('Proof', (tester) async {
    final BehaviorSubject<int> subject = BehaviorSubject.seeded(99);
    final valueFinder = find.text('Value = 99');
    final noneFinder = find.text('None');
    await tester.pumpWidget(
      MaterialApp(
        home: SomeWidget(stream: subject),
      ),
    );
    print('Looking for None');
    expect(valueFinder, findsNothing);
    expect(noneFinder, findsOneWidget);
    await tester.pumpAndSettle();
    print('Looking for Value = 99');
    expect(valueFinder, findsOneWidget);
    expect(noneFinder, findsNothing);
  });
}

class SomeWidget extends StatelessWidget {
  const SomeWidget({Key? key, required this.stream}) : super(key: key);

  final Stream<int> stream;

  @override
  Widget build(BuildContext context) => FutureBuilder<int>(
        future: stream.first
            .timeout(const Duration(milliseconds: 10))
            .onError<TimeoutException>((e, st) => -1),
        builder: (context, snapshot) => Center(
          child: Text(
            snapshot.hasData ? 'Value = ${snapshot.data}' : 'None',
          ),
        ),
      );
}

Note

According to this issue, if we wrap pumpWidget within runAsync, it actually works. However, it doesn't feel right. It is more like a workaround.

The question is, can this issue be resolved? If not, or if you believe this is not an issue, could you clarify why such behavior is legit.

Thanks in advance.

CaleyD commented 1 year ago

We are running into this issue as well