How to write widget test for CachedNetworkImage in flutter
The CachedNetworkImage
widget accepts an optional cacheManager
property (default to a DefaultCacheManager instance).
The idea is to use get_it to inject a DefaultCacheManager
instance in your app, and a custom one in the test. This custom cache manager can return a test asset file.
Custom cache manager
import 'dart:io';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_cache_manager/src/cache_store.dart';
import 'package:flutter_cache_manager/src/storage/non_storing_object_provider.dart';
class TestCacheManager extends BaseCacheManager {
TestCacheManager()
: super(
null,
cacheStore: CacheStore(
Future.value(null),
null,
null,
null,
cacheRepoProvider: Future.value(NonStoringObjectProvider()),
),
);
@override
Future<String> getFilePath() async {
return null;
}
@override
Stream<FileResponse> getFileStream(String url,
{Map<String, String> headers, bool withProgress}) async* {
if (url == 'https://myownservice.com/example') {
yield FileInfo(
File('test/assets/mock_image.jpg'),
FileSource.Cache,
DateTime(2050),
url,
);
}
}
}
Widget you want to test:
class MyImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CachedNetworkImage(
imageUrl: 'https://myownservice.com/example',
cacheManager: GetIt.instance.get<BaseCacheManager>(),
);
}
}
App main()
:
GetIt.instance.registerSingleton<BaseCacheManager>(
DefaultCacheManager(),
);
Test main()
:
GetIt.instance.registerSingleton<BaseCacheManager>(
TestCacheManager(),
);
Then you should be able to pump an instance of the MyImage
widget:
await tester.pumpWidget(MyImage());
// Important: you need to pump an other frame so the CachedNetworkImage
// can replace the placeholder by the image received from the
// cache manager stream.
await tester.pump();
See this blog post for full explanations, code sample and example app.
Admin
Updated on December 16, 2022Comments
-
Admin over 1 year
I can widget test Image.network using HttpOverrides and tried following code to test CachedNetworkImage with no success Is there anybody who tested this package already? I've also tried to use sqflite using setMockMethodCallHandler to MethodChannel('com.tekartik.sqflite') but just getDatabasesPath method get called what is the correct approach to test this package?
import 'dart:async'; import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import '../unit_test/sqlcool.dart'; const List<int> kTransparentImage = <int>[ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0D, 0x0A, 0x2D, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, ]; void main() async { TestWidgetsFlutterBinding.ensureInitialized(); await setup(); final MockHttpClient client = MockHttpClient(); final MockHttpClientRequest request = MockHttpClientRequest(); final MockHttpClientResponse response = MockHttpClientResponse(); final MockHttpHeaders headers = MockHttpHeaders(); testWidgets('Headers', (WidgetTester tester) async { HttpOverrides.runZoned<Future<void>>(() async { // await tester.pumpWidget(Image.network( // 'https://www.example.com/images/frame.png', // headers: const <String, String>{'flutter': 'flutter'}, // )); await tester.pumpWidget(CachedNetworkImage( imageUrl: 'https://www.example.com/images/frame.png', errorWidget: (context, err, o) { print( "===========>>>>> CachedNetworkImage error= $err <<<<================="); })); }, createHttpClient: (SecurityContext _) { when(client.getUrl(any)).thenAnswer((invocation) { print( "================>>>>>> getUrl = ${invocation.positionalArguments} <<<<<==============="); return Future<HttpClientRequest>.value(request); }); when(request.headers).thenReturn(headers); when(request.close()).thenAnswer((invocation) { print( "================>>>>>> request.close = ${invocation.toString()} <<<<<==============="); return Future<HttpClientResponse>.value(response); }); when(response.contentLength).thenReturn(kTransparentImage.length); when(response.statusCode).thenReturn(HttpStatus.ok); when(response.listen(any)).thenAnswer((Invocation invocation) { final void Function(List<int>) onData = invocation.positionalArguments[0] as void Function(List<int>); print( "================>>>>>> onData = ${onData} <<<<<==============="); final void Function() onDone = invocation.namedArguments[#onDone] as void Function(); print( "================>>>>>> onDone = ${onDone} <<<<<==============="); final void Function(Object, [StackTrace]) onError = invocation .namedArguments[#onError] as void Function(Object, [StackTrace]); final bool cancelOnError = invocation.namedArguments[#cancelOnError] as bool; return Stream<List<int>>.fromIterable(<List<int>>[kTransparentImage]) .listen(onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); }); return client; }); }, skip: isBrowser); } class MockHttpClient extends Mock implements HttpClient {} class MockHttpClientRequest extends Mock implements HttpClientRequest {} class MockHttpClientResponse extends Mock implements HttpClientResponse {} class MockHttpHeaders extends Mock implements HttpHeaders {} Directory directory; const MethodChannel channel = MethodChannel('com.tekartik.sqflite'); final List<MethodCall> log = <MethodCall>[]; bool setupDone = false; Future<void> setup() async { // WidgetsFlutterBinding.ensureInitialized(); if (setupDone) { return; } directory = await Directory.systemTemp.createTemp(); String response; channel.setMockMethodCallHandler((MethodCall methodCall) async { print("METHOD CALL: $methodCall"); log.add(methodCall); switch (methodCall.method) { case "getDatabasesPath": return directory.path; break; case "query": return 1; break; } return response; }
-
Rob over 2 yearsIt seems that the blog post is out of date and now there is no example on how to do this.