Flutter Widget Tests with NetworkImage

flutter network image placeholder
flutter cached network image
flutter preload network image
flutter image.network not working
flutter test network image.
flutter widget testing
flutter image_test_utils
flutter widget test navigation

I have a Widget with NetworkImage (so far with hard-coded url). I would like to widget test this Widget, but I got 404 when I run widget test (url is 100% valid). How can I make NetworkImages load themselves or (which would be better) ignore them so that my tests won't fail because of 404?

In widget tests, the default HTTP client has been replaced with one that always returns 400s. There's a sample on how to do this in the flutter_markdown repo along with couple other places. I used to copy and paste this to every project, but I did it enough times to get quite bored.

There's now a library for this (by me), called "image_test_utils". You can wrap your widget tests with a provideMockedNetworkImages method, which replaces the mocked HTTP client with one that always returns transparent images. Which in turn makes your tests pass.

pubspec.yaml:

dev_dependencies:
  image_test_utils: ^1.0.0

my_image_test.dart:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:image_test_utils/image_test_utils.dart';

void main() {
  testWidgets('my image test', (WidgetTester tester) async {
    provideMockedNetworkImages(() async {
      /// Now we can pump NetworkImages without crashing our tests. Yay!
      await tester.pumpWidget(
        MaterialApp(
          home: Image.network('https://example.com/image.png'),
        ),
      );

      /// No crashes.
    });
  });
}

Testing tidbit #2, When the exception was thrown, this was the stack: #0 NetworkImage.​_loadAsync (package:flutter/src/painting/image_provider.dart:490:7) <  The flutter_test package provides the following tools for testing widgets: The WidgetTester , which allows building and interacting with widgets in a test environment. The testWidgets () function, which automatically creates a new WidgetTester for each test case, The Finder classes, which

I use

import 'package:flutter/services.dart' show createHttpClient;

final imageUri = Uri.parse('http://example.com$dummyImagePath');

testWidgets( ...) {
  createHttpClient = createMockImageHttpClient;

  await tester.pumpWidget(new TestWrapperWidget(
    child: (_) => new ImageWidget(name: text, url: imageUri)));

}
import 'dart:async' show Future;

import 'package:http/http.dart' show Client, Response;
import 'package:http/testing.dart' show MockClient;
import 'dummy_image_data.dart'
    show dummyImageData;

const String dummyImagePath = '/image.jpg';
Client createMockImageHttpClient() => new MockClient((request) {
      switch (request.url.path) {
        case dummyImagePath:
          return new Future<Response>.value(new Response.bytes(
              dummyImageData, 200,
              request: request, headers: {'Content-type': 'image/jpg'}));
        default:
          return new Future<Response>.value(new Response('', 404));
      }
    });
Uint8List get dummyImageData => BASE64.decode(dummyJpgImageBase64);    

(I created the image data Base64 using http://base64.wutils.com/encoding-online/)

const String dummyAvatarJpgImageBase64 =
'/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIi'
...   
'itf93F+MLRdehP4ZutvWj8m+rjzpz//Z';

This way the test also works when I start it with flutter run -t test/image_test.dart, but the image data can also be just served from an image file for normal test runs.

Using the mockito package

image_mock_http_client.dart

import 'dart:async' show Future, Stream;
import 'dart:io'
    show
        HttpClient,
        HttpClientRequest,
        HttpClientResponse,
        HttpHeaders,
        HttpOverrides,
        HttpStatus,
        SecurityContext;

import '.dummy_image_data.dart';
import 'package:mockito/mockito.dart'
    show Mock, any, anyNamed, captureAny, throwOnMissingStub, when;

const String dummyAvatarImagePath = '/avatar.jpg';

class TestHttpOverrides extends HttpOverrides {
  TestHttpOverrides(this.data);

  final Map<Uri, List<int>> data;

  @override
  HttpClient createHttpClient(SecurityContext context) =>
      createMockImageHttpClient(context, data);
}

// Returns a mock HTTP client that responds with an image to all requests.
MockHttpClient createMockImageHttpClient(
    SecurityContext _, Map<Uri, List<int>> data) {
  final client = new MockHttpClient();
  final request = new MockHttpClientRequest();
  final response = new MockHttpClientResponse(data);
  final headers = new MockHttpHeaders();

  throwOnMissingStub(client);
  throwOnMissingStub(request);
  throwOnMissingStub(response);
  throwOnMissingStub(headers);

  when<dynamic>(client.getUrl(captureAny)).thenAnswer((invocation) {
    response.requestedUrl = invocation.positionalArguments[0] as Uri;
    return new Future<HttpClientRequest>.value(request);
  });

  when(request.headers).thenAnswer((_) => headers);

  when(request.close())
      .thenAnswer((_) => new Future<HttpClientResponse>.value(response));

  when(response.contentLength)
      .thenAnswer((_) => data[response.requestedUrl].length);

  when(response.statusCode).thenReturn(HttpStatus.ok);

  when(
    response.listen(
      any,
      cancelOnError: anyNamed('cancelOnError'),
      onDone: anyNamed('onDone'),
      onError: anyNamed('onError'),
    ),
  ).thenAnswer((invocation) {
    final onData =
        invocation.positionalArguments[0] as void Function(List<int>);

    final onDone = invocation.namedArguments[#onDone] as void Function();

    final onError = invocation.namedArguments[#onError] as void Function(Object,
        [StackTrace]);

    final cancelOnError = invocation.namedArguments[#cancelOnError] as bool;

    return new Stream<List<int>>.fromIterable([data[response.requestedUrl]])
        .listen(onData,
            onDone: onDone, onError: onError, cancelOnError: cancelOnError);
  });
  return client;
}

class MockHttpClient extends Mock implements HttpClient {}

class MockHttpClientRequest extends Mock implements HttpClientRequest {}

class MockHttpClientResponse extends Mock implements HttpClientResponse {
  MockHttpClientResponse(this.data);
  final Map<Uri, List<int>> data;
  Uri requestedUrl;

  @override
  Future<S> fold<S>(S initialValue, S combine(S previous, List<int> element)) =>
      new Stream.fromIterable([data[requestedUrl]]).fold(initialValue, combine);
}

class MockHttpHeaders extends Mock implements HttpHeaders {}

my_test.dart

import 'image_mock_http_client.dart' show TestHttpOverrides;

...

  setUp(() async {
    HttpOverrides.global = new TestHttpOverrides({
      'http://example.com/my_image.png':               dummyAvatarImageData,
      'http://example.com/other_image.png: dummyPngImageData,
    });
  });

dummyAvatarImageData and dummyPngImageData are list<int> and contain the image data.

Display images from the internet, One useful thing about the Image widget: It supports animated gifs. content_copy. Image.network( 'https://github.com/flutter  Flutter Widget Tests with NetworkImage. 2. Dart / flutter: download or read the contents of a Google Drive file. 4. Trying to load an image using NetworkImage fails

If you have this really unusual situation where the widget test is all about whether the images get correctly fetched, you can undo the override.

For every test:

setUpAll(() => HttpOverrides.global = null);

For a single test:

testWidgets('Image gets correctly fetched.', () {
  HttpOverrides.runZoned(
    // Run your tests.
    () {},
    createHttpClient: (securityContext) => MockHttpClient(securityContext),
  );
});

network_image_mock, Network Image Mock #. Pub Version Lint & Test codecov.io. A utility for providing mocked response to Image.network in Flutter widget tests. Pump a NetworkImage widget in a widget test; await tester.pumpWidget(NetworkImage(/* an image url */); An NetworkImageLoadException should be thrown (in the default test platform), but on the Chrome platform no exception is thrown. Target Platform: Web Target OS version/browser: Chrome Devices: Logs Analyzing app_flutter

image_test_utils, Without providing mocked responses, any widget test that pumps up import '​package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import Now we can pump NetworkImages without crashing our tests. Just see your widget tests with Image.network widgets pass with flying colors. Behind the scenes, wrapping your code with provideMockedNetworkImages creates a new Zone, in which the default HTTP client has been replaced with a mocked one. The mocked client always responds with a transparent image and a 200 - OK status.

NetworkImage does not throw NetworkImageLoadException in , Steps to Reproduce Pump a NetworkImage widget in a widget test await Flutter plugin not installed; this adds Flutter specific functionality. Flutter Advanced Network Image Provider # An advanced image provider provides caching and retrying for flutter app. Now with zoomable widget and transition to image widget. Getting Started # Installation # Add this to your pubspec.yaml (or create it): dependencies: flutter_advanced_networkimage: any Then run the flutter tooling: flutter

flutter/flutter, Hello, my simple test crashes when using Image.network. While this When the exception was thrown, this was the stack: #0 NetworkImage. _resolveImage (​package:flutter/src/widgets/image.dart:581:20) #8 _ImageState. Widget tests are the tests to check if a widget is created as expected and also to check if widget interactions work as expected. The creation process of the widget tests is similar to unit tests. Extra to unit test, we use the WidgetTester utility that Flutter provides for testing the widgets.

Comments
  • Looks awesome! I will definitely try this out!
  • I had placed createHttpClient = createMockImageHttpClient inside setUpAll and then worked like a charm! :) Thanks! :)
  • I believe createHttpClient is deprecated in newer versions of flutter. See github.com/flutter/flutter/issues/15447. Do you know what the recommended way of achieving this is now?
  • @MarcinSzałek, Could you show full code of your setUpAll function?
  • @hunghd I added another example that uses the mockito package.
  • @hunghd Sorry, but at this moment my code for this doesn't seem to work. Did you manage to use Gunter's one? @GünterZöchbauer Can you compile your code? It looks like TestHttpOverrides constructor expects Uri as keys in map, and you pass strings and gives me an error...