Search code examples
flutterflutter-blocwidget-test-flutter

Flutter Bloc Widget testing how to find.text under If statement in bloc pattern


I am trying to Widget test my WelcomeScreen(). WelcomeScreen has a BlocProvider and a BlocBuilder. After I load WelcomeBloc() it checks with an if statement inside the builder to check if the state is WelcomeLoadSuccessState.

How do I find something under the if statement if the statement is true?

My Welcome screen:

Widget build(BuildContext context) {
  return BlocProvider(
   create: (context) => WelcomeBloc(),
   child: BlocBuilder<WelcomeBloc, WelcomeState>(
     builder: (context, state) {
       if (state is WelcomeLoadSuccessState) {
         return Scaffold(
           body: Container(
            child: Column(
              children: [
                Wrap(
                  direction: Axis.vertical,
                  crossAxisAlignment: WrapCrossAlignment.center,
                  children: [
                    Padding(
                      padding: EdgeInsets.all(8),
                      child: ShowUp(
                          delay: _delay + 200,
                          child: Text('Welcome user’,        // <——— I want to find this one
                              )),
                    ),
                  
                  ],
                ),
              ],
            )),
      );
    }

    // return LoadingWidget();
    return Text('Something');       // <——— This one I can find
  },
),
);
}

The test that I have now:

    main() {
  WelcomeBloc welcomeBloc;
  WelcomeService welcomeService;
  final Brand brand = Brand();

  setUp(() {
    setUpMocks();
    welcomeService = localServices<WelcomeService>();
    welcomeBloc = MockWelcomeBloc();
  });

 _createWidget(WidgetTester tester) async {
    when(welcomeService.getBrand(id: '609a88d324a01928242d1ca9')).thenAnswer((realInvocation) => Future.value(brand));
    welcomeBloc.add(WelcomeLoadRequestEvent(id: '609a88d324a01928242d1ca9'));
    when(welcomeBloc.state).thenAnswer((_) => WelcomeLoadSuccessState(brand: brand));

    print(welcomeBloc.state); //Correct State (WelcomeLoadSuccessState)

    await tester.pumpWidget(
        MaterialApp(
          title: 'Flutter Demo',
          home: WelcomeScreen(),
        )
    );

    await tester.pump();
  }

  testWidgets('Welcome Screen Test', (WidgetTester tester) async {
    await _createWidget(tester);
    await tester.pump();

    //expect(find.textContaining('Welcome user'), findsOneWidget); //What I want
    expect(find.text('Something'), findsOneWidget);  //This works
  });

  tearDown(() {
    welcomeBloc?.close();
  });
}

Thank you for helping.


Solution

  • I solved it:

    change:

    create: (context) => WelcomeBloc()
    

    to:

    create: (context) => WelcomeBloc()..add(WelcomeLoadRequestEvent(id: '609a88d324a01928242d1ca9')),
    

    and my test is now this:

    main() {
      WelcomeBloc welcomeBloc;
      WelcomeService welcomeService;
      final Brand brand = Brand();
    
      setUp(() {
        setUpMocks();
        welcomeService = localServices<WelcomeService>();
        welcomeBloc = MockWelcomeBloc();
      });
    
      _createWidget(WidgetTester tester) async {
        await tester.pumpWidget(MaterialApp(
          title: 'Flutter Demo',
          home: WelcomeScreen(),
        ));
    
       await tester.pump(Duration(seconds: 10));
      }
    
      testWidgets('Welcome Screen Test', (WidgetTester tester) async {
        when(welcomeService.getBrand(id: '609a88d324a01928242d1ca9'))
            .thenAnswer((realInvocation) => Future.value(brand));
        whenListen(
            welcomeBloc,
            Stream.fromIterable([
          WelcomeLoadInProgressState(),
          WelcomeLoadSuccessState(brand: brand),
        ]));
    
        await _createWidget(tester);
        await tester.pump(Duration(seconds: 5));
    
        expect(find.textContaining('Welcome user'), findsOneWidget);
      });
    
     tearDown(() {
        welcomeBloc?.close();
        unRegister();
      });
    }
    

    Edit to add:

    For my other pages it was useful to separate the blocProvider and the blocBuilder. This way I was able to Mock my blocProvider with a MockMyBloc() and then give the screen in the child.

    My real widgets:

    MyWidgetMakeBlocProviders(
        Widget build(context) {
            return BlocProvider<MyBloc>(
                create: (context) => MyBloc(),
                child: MyScreen(),
            );
        }
    )
    
    
    MyScreen(
        Widget build(context) {
            return BlocBuilder<MyBloc, MyBlocState>(
                builder: (context, state) {...}
            );
        }
    )
    

    My test:

    testWidgets('', (tester) async {
        whenListen(MockMyBloc, Stream.fromIterable([
            InitState(),
            LoadedState()
        ]));
    
        await _createWidget(tester);
        await tester.pump();
        
        //expect()
    });
    
    _createWidget(tester) async {
        await tester.pumpWidget(
            MaterialApp(
                title: '',
                home: BlocProvider<MockMyBloc>(
                    create: (context) => MockMyBloc(),
                    child: MyScreen(),
                )
            )
        );
    
        await tester.pump();
    }