androidfluttertestingwidget-test-flutter

Cannot Find AssetImage In BoxDecoration In Flutter Widget Tests


I am trying to create widget splash screen that shows when the app loads and the native splash screen is shown. I created the splash screen and everything seems fine so i decide to create widget test for the splash screen background image and single text on the screen. The test part for the text passes but i have issues with the image.

Widget test error text:

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: exactly one matching node in the widget tree
  Actual: _WidgetTypeFinder:<zero widgets with type "Image" (ignoring offstage widgets)>
   Which: means none were found but one was expected

The full splash screen code:

class SplashScreenPage extends StatefulWidget {
  const SplashScreenPage({Key? key}) : super(key: key);

  @override
  State<SplashScreenPage> createState() => _SplashScreenPageState();
}

class _SplashScreenPageState extends State<SplashScreenPage> {
  Timer? _timer;

  @override
  void initState() {
    _timer = Timer(const Duration(seconds: 4), () {
      Navigator.of(context).pushReplacementNamed(RoutingConst.defaultRoute);
    });
    super.initState();
  }

  @override
  void dispose() {
    _timer!.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
        Container(
          decoration: const BoxDecoration(
            image: DecorationImage(
              image: AssetImage("assets/images/background.png"),
              fit: BoxFit.cover,
            ),
          ),
        ),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: Column(
            children: [
              const Spacer(),
              const Spacer(),
              const Spacer(),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Flexible(
                    child: Text(
                      "SPLASH SCREEN TEXT",
                      maxLines: 2,
                      overflow: TextOverflow.ellipsis,
                      textAlign: TextAlign.center,
                      style: GoogleFonts.inter(
                        color: Colors.white,
                        fontSize: 40,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
                ],
              ),
              const Spacer(),
            ],
          ),
        ),
      ]
      ),
    );
  }
}

The widget test:

void main() {
  //
  // Setup
  //

  Widget createWidgetUnderTest() {
    return const MaterialApp(
      home: SplashScreenPage(),
    );
  }

  //
  // Testing
  //

  testWidgets(
    "Check if splash screen text shows up",
    (WidgetTester tester) async {
      await tester.pumpWidget(createWidgetUnderTest());
      expect(find.text("SPLASH SCREEN TEXT"), findsOneWidget);
    },
  );

  testWidgets(
    "Check if splash screen background shows up",
    (WidgetTester tester) async {
      await tester.pumpWidget(createWidgetUnderTest());
      expect(find.byType(Image), findsOneWidget);
    },
  );
}

The test for the text passes, but the one for the image fails and i can't figure out why. I tried the find.image(AssetImage("asset-path")) but that didn't work aswell. Tried to put key on the container holding the box decoration in and then try to find the container in the test and it finds it so maybie i am doing something wrong in finding image inside box decoration but i don't know how to find it if that's the case.


Solution

  • The Problem:

    The SplashScreenPage does not contain an actual Image widget, so the finders below used in the test will fail to find an Image widget.

    Setting an image to a Container's decoration does not create an Image widget, so the finders are inappropriate for this test.

    The Solution:

    Use find.byWidgetPredicate to find the actual Widget containing the image, in this case: Container and add checks to specify the properties of the Container you're looking for.

    Here's the code:

     testWidgets(
        "Check if splash screen background shows up",
        (WidgetTester tester) async {
          await tester.pumpWidget(createWidgetUnderTest());
    
          final assetImageFinder = find.byWidgetPredicate(
            (Widget widget) {
              if (widget is Container) {
                final decoration = widget.decoration;
    
                if (decoration is BoxDecoration) {
                  final image = decoration.image;
    
                  if (image is DecorationImage) {
                    final imageProvider = image.image;
    
                    if (imageProvider is AssetImage) {
                      return imageProvider.assetName ==
                          "assets/images/background.png";
                    }
                  }
                }
              }
    
              return false;
            },
          );
    
          expect(assetImageFinder, findsOneWidget);
        },
      );