The following NetworkImageLoadException was thrown resolving an image codec

Flutter Jun 17, 2023

During Flutter widget tests, it is possible to encounter a NetworkImageLoadException error when using the Image.network widget. This error occurs because Image.network attempts to make real network calls while running the tests, which is not permitted in a test environment according to Flutter's guidelines.

To prevent this, Flutter automatically returns empty responses with a status code of 400 (Bad Request) when actual HTTP requests are made during testing. However, in this post, we explore different solutions to overcome this limitation and successfully test network images in Flutter.

flutter_example_of_network_image_load_exception

Setting up

In order to explore the solutions we will start by creating the following main.dart file, which includes a network.image widget :

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Network Image Example',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Network Image Example'),
      ),
      body: Center(
        child: Image.network(
          'https://picsum.photos/id/182/350/250',
        ),
      ),
    );
  }
}

This code snippet demonstrates a basic Flutter application that displays a network image using the Image.network widget. It sets up the app with a title and a home page, which contains the Image.network widget. The Image.network widget fetches and displays an image from a remote server using the provided URL. In this case, it fetches an image from picsum.photos.

flutter_basic_application_showing_network_image

Failing test case

Now that we have our application, we can write the following widget test to test our Image.network widget:

import 'package:codeonwards_demo/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Network Image Test', (WidgetTester tester) async {
    await tester.pumpWidget(
      const MaterialApp(
        home: MyHomePage(),
      ),
    );

    Finder image = find.byType(Image);
    Image imageWidget = tester.widget(image);
    final networkImageWidget = imageWidget.image as NetworkImage;

    expect(image, findsOneWidget);
    expect(imageWidget.image, isA<NetworkImage>());
    expect(networkImageWidget.url, 'https://picsum.photos/id/182/350/250');
  });
}

In this code snippet, we have a widget test for the Image.network widget. We create a test case with MyHomePage wrapped in MaterialApp, locate the Image widget using the find.byType function, extract the NetworkImage, and assert expectations for the image's presence and URL.

However, when we run this test, it will fail because as mentioned in the introduction, it will attempt to make a real network call, which is not allowed by Flutter's guidelines.

flutter_unsuccessfull_test_showing_network_image_load_exception

1. Easy Fix without using any Package

To address the error without relying on any external package, you can resolve it by disabling custom HTTP overrides and allowing the Image.network widget to use the default networking behavior provided by Flutter. Here is the updated test code:

import 'dart:io';

import 'package:codeonwards_demo/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  setUpAll(() => HttpOverrides.global = null);

  testWidgets('Network Image Test', (WidgetTester tester) async {
    await tester.pumpWidget(const MaterialApp(home: MyHomePage()));
    await tester.pumpAndSettle();

    Finder image = find.byType(Image);
    Image imageWidget = tester.widget(image);
    NetworkImage networkImageWidget = imageWidget.image as NetworkImage;

    expect(image, findsOneWidget);
    expect(imageWidget.image, isA<NetworkImage>());
    expect(networkImageWidget.url, 'https://picsum.photos/id/182/350/250');
  });
}

In this updated version, we import dart:io to access the HttpOverrides.global property. By setting HttpOverrides.global to null within the setUpAll function, we reset the global HttpOverrides object to its default value. This ensures that the default networking behavior is used during the widget tests.

As a result, Flutter no longer returns empty responses with a status code of 400 (Bad Request) when actual HTTP requests are made. Additionally, tester.pumpAndSettle() is added to ensure the widget finishes building before proceeding with the tests.

2. Using the mocktail_image_network Package

Another approach is to use the mocktail_image_network package, which provides a convenient way to mock network images during widget testing. Here are the steps to use this package:

Step 1: Add the Package to your Dev Dependencies
Execute the following command to add the mocktail_image_network package to your project: flutter pub add mocktail_image_network --dev.

Make sure that your dev_dependencies in pubspec.yaml include the mocktail_image_network package:

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0
  mocktail_image_network: ^0.3.1

Step 2: Update the Widget Test
Now that we have the package installed we can change our widget test to look like this:

import 'package:codeonwards_demo/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail_image_network/mocktail_image_network.dart';

void main() {
  testWidgets('Network Image Test', (WidgetTester tester) async {
    await mockNetworkImages(
      () async => await tester.pumpWidget(
        const MaterialApp(
          home: MyHomePage(),
        ),
      ),
    );

    Finder image = find.byType(Image);
    Image imageWidget = tester.widget(image);
    NetworkImage networkImageWidget = imageWidget.image as NetworkImage;

    expect(image, findsOneWidget);
    expect(imageWidget.image, isA<NetworkImage>());
    expect(networkImageWidget.url, 'https://picsum.photos/id/182/350/250');
  });
}

In this updated version we import the mocktail_image_network package, which provides the necessary functionality for mocking network images. Next, we wrap the widget creation process with the mockNetworkImages function. This ensures that network calls made by the Image.network widget are intercepted and replaced with mock responses during testing. By doing so, we can simulate network behavior and verify the expected behavior of our widget.

3. Using the network_image_mock Package

Alternatively, you can use the network_image_mock package, which provides similar capabilities for mocking network images in widget tests. Here are the steps to use this package:

Step 1: Add the Package to your Dev Dependencies
Execute the following command to add the network_image_mock package to your project: flutter pub add network_image_mock --dev.

Make sure that your dev_dependencies in pubspec.yaml include the network_image_mock package:

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0
  network_image_mock: ^2.1.1

Step 2: Update the Widget Test:
Now that we have the package installed we can change our widget test to look like this:

import 'package:codeonwards_demo/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:network_image_mock/network_image_mock.dart';

void main() {
  testWidgets('Network Image Test', (WidgetTester tester) async {
    await mockNetworkImagesFor(
      () async => await tester.pumpWidget(
        const MaterialApp(
          home: MyHomePage(),
        ),
      ),
    );

    Finder image = find.byType(Image);
    Image imageWidget = tester.widget(image);
    NetworkImage networkImageWidget = imageWidget.image as NetworkImage;

    expect(image, findsOneWidget);
    expect(imageWidget.image, isA());
    expect(networkImageWidget.url, 'https://picsum.photos/id/182/350/250');
  });
}

The changes made to the original test are similar to the previous solution, we import the network_image_mock package and wrap our widget creation with the mockNetworkImagesFor function. By using the mockNetworkImagesFor function provided by network_image_mock, we can mock the network images and ensure that the network calls are intercepted and replaced with mock responses during the widget test.

Recommendation

When dealing with the NetworkImageLoadException error in Flutter widget tests, consider the specific needs of your testing environment. Disabling custom HTTP overrides to rely on Flutter's default networking behavior may offer a quick solution, but it has limitations and compromises test isolation. Making actual HTTP calls during tests is discouraged due to potential side effects and dependencies on external systems.

Mocking
For more control and thorough testing, use the specialized packages mocktail_image_network or network_image_mock. These packages enable you to mock network images, simulate different network scenarios, and ensure accurate testing while eliminating the likelihood of encountering the NetworkImageLoadException error.

Choosing the right package
In order to choose the right package choose the package that matches your testing dependencies.

Conclusion

In this post, we explored different approaches to fix the NetworkImageLoadException error that occurs when using the Image.network widget in Flutter widget tests. We discussed disabling custom HTTP overrides, using the mocktail_image_network package, and using the network_image_mock package.

Depending on your specific use case and preferences, you can choose the approach that best fits your needs. By applying these fixes, you can successfully test your widget's behavior involving network images without encountering the NetworkImageLoadException error.

Tags