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?

- 4,609
- 5
- 30
- 43
5 Answers
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.
});
});
}

- 4,959
- 5
- 38
- 42
-
Looks awesome! I will definitely try this out! – Marcin Szałek Oct 04 '18 at 08:48
-
I've implemented the `frameBuilder` of the `Network.Image` and its never getting called properly, the frame field is always null – Arlind Hajredinaj Apr 24 '20 at 08:47
-
2This library is no longer maintained. I recommend not using it. – Paulo Henrique Nonaka Sep 30 '20 at 21:23
-
1please, update your lib to support null safety – Luiz Negrini Mar 04 '21 at 21:21
-
Has the same library been tested with integration tests? They use the same API as widget tests – Christian Findlay Feb 22 '23 at 04:40
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.', (tester) {
HttpOverrides.runZoned(
// Run your tests.
() {},
createHttpClient: (securityContext) => MockHttpClient(securityContext),
);
});

- 7,719
- 2
- 35
- 52
-
1That one worked perfectly for me. I don't know how safe it is to just override stuff, but it's simpler than mocking everything. – Oct 10 '20 at 14:31
A few years later and now that image_test_utils package seems no longer maintained, here is another easy solution to this problem.
I used the network_image_mock package (supports nullsafety) and added just two lines of code to my test. Wrap your pumpWidget
call with mockNetworkImagesFor
like this and you won't get the image loading errors anymore:
mockNetworkImagesFor(() => tester.pumpWidget(makeTestableWidget()));

- 4,036
- 43
- 52
-
This should totally be the newer accepted answer, since that package isn't maintained anymore. – venir May 16 '22 at 10:42
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.

- 623,577
- 216
- 2,003
- 1,567
-
1I had placed `createHttpClient = createMockImageHttpClient` inside `setUpAll` and then worked like a charm! :) Thanks! :) – Marcin Szałek Mar 27 '18 at 15:29
-
1I believe `createHttpClient` is deprecated in newer versions of flutter. See https://github.com/flutter/flutter/issues/15447. Do you know what the recommended way of achieving this is now? – Georg Grab Jun 22 '18 at 08:15
-
-
@hunghd I added another example that uses the mockito package. – Günter Zöchbauer Jul 09 '18 at 10:55
-
@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... – Marcin Szałek Jul 13 '18 at 18:03
-
Oerhaps I copied the wrong code line. Just use `Uri.parse('theUrl')` to get an Uri instead of string.(Not on my computer to check) – Günter Zöchbauer Jul 13 '18 at 18:34
-
Do I need to use a Base64 encoded image to make it pass in the console? I've tried both, loading from assets and encoded but: 1. from the assets, I just get an error that the file can't be loaded 2. encoded image, the test pass but only in with "flutter run ..." in console (the image is not shown, but it allows me to test the Widget without failing before) – ngbl Sep 24 '18 at 16:21
-
No, without Base64 you can omit `Uint8List get dummyImageData => BASE64.decode(dummyJpgImageBase64);` I don't know if loading from assets is supported. I have it as Base64 encoded string in Dart code. – Günter Zöchbauer Sep 24 '18 at 16:27
-
1Since TestHttpOverrides() deprecated, used MockHttpOverrides() and it worked – SardorbekR Nov 17 '22 at 08:50
I updated the code from Günter Zöchbauer answer using null safety and the mocktail package.
image_mock_http_client.dart
import 'dart:io';
import 'package:mocktail/mocktail.dart';
class MockHttpOverrides extends HttpOverrides {
MockHttpOverrides(this.data);
final Map<Uri, List<int>> data;
@override
HttpClient createHttpClient(SecurityContext? context) {
final client = MockHttpClient();
final request = MockHttpClientRequest();
final response = MockHttpClientResponse(data);
final headers = MockHttpHeaders();
/// Comment the exception when stub is missing from client
/// because it complains about missing autoUncompress stub
/// even setting it up as shown bellow.
// throwOnMissingStub(client);
throwOnMissingStub(request);
throwOnMissingStub(response);
throwOnMissingStub(headers);
// This line is not necessary, it can be omitted.
when(() => client.autoUncompress).thenReturn(true);
// Use decompressed, otherwise you will get bad data.
when(() => response.compressionState)
.thenReturn(HttpClientResponseCompressionState.decompressed);
// Capture the url and assigns it to requestedUrl from MockHttpClientResponse.
when(() => client.getUrl(captureAny())).thenAnswer((invocation) {
response.requestedUrl = invocation.positionalArguments[0] as Uri;
return Future<HttpClientRequest>.value(request);
});
// This line is not necessary, it can be omitted.
when(() => request.headers).thenAnswer((_) => headers);
when(() => request.close())
.thenAnswer((_) => Future<HttpClientResponse>.value(response));
when(() => response.contentLength)
.thenAnswer((_) => data[response.requestedUrl]!.length);
when(() => response.statusCode).thenReturn(HttpStatus.ok);
when(
() => response.listen(
captureAny(),
cancelOnError: captureAny(named: 'cancelOnError'),
onDone: captureAny(named: 'onDone'),
onError: captureAny(named: '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 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;
// It is not necessary to override this method to pass the test.
@override
Future<S> fold<S>(
S initialValue,
S Function(S previous, List<int> element) combine,
) {
return Stream.fromIterable([data[requestedUrl]])
.fold(initialValue, combine as S Function(S, List<int>?));
}
}
class MockHttpHeaders extends Mock implements HttpHeaders {}
my_test.dart
const _imageUrl = 'https://your.image.uri.here';
void main() {
setUp(() async {
registerFallbackValue(Uri());
// Load an image from assets and transform it from bytes to List<int>
final _imageByteData = await rootBundle.load('assets/images/image.png');
final _imageIntList = _imageByteData.buffer.asInt8List();
final _requestsMap = {
Uri.parse(_imageUrl): _imageIntList,
};
HttpOverrides.global = MockHttpOverrides(_requestsMap);
});
...
}

- 474
- 5
- 6