如何在颤动中为 CachedNetworkImage 编写小部件测试
Posted
技术标签:
【中文标题】如何在颤动中为 CachedNetworkImage 编写小部件测试【英文标题】:How to write widget test for CachedNetworkImage in flutter 【发布时间】:2020-04-11 23:58:37 【问题描述】:我可以使用 HttpOverrides 小部件测试 Image.network 并尝试以下代码来测试 CachedNetworkImage 没有成功 有没有人已经测试过这个包? 我也尝试使用 sqflite 使用 setMockMethodCallHandler 到 MethodChannel('com.tekartik.sqflite') 但只是调用 getDatabasesPath 方法 测试这个包的正确方法是什么?
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;
【问题讨论】:
@Moussa 你的问题解决了吗?我也面临同样的问题 【参考方案1】:CachedNetworkImage
小部件接受可选的cacheManager
属性(默认为DefaultCacheManager 实例)。
这个想法是使用get_it 在您的应用程序中注入一个DefaultCacheManager
实例,并在测试中使用一个自定义实例。这个自定义缓存管理器可以返回一个测试资产文件。
自定义缓存管理器
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,
);
您要测试的小部件:
class MyImage extends StatelessWidget
@override
Widget build(BuildContext context)
return CachedNetworkImage(
imageUrl: 'https://myownservice.com/example',
cacheManager: GetIt.instance.get<BaseCacheManager>(),
);
应用main()
:
GetIt.instance.registerSingleton<BaseCacheManager>(
DefaultCacheManager(),
);
测试main()
:
GetIt.instance.registerSingleton<BaseCacheManager>(
TestCacheManager(),
);
那么您应该能够抽取MyImage
小部件的实例:
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();
请参阅此blog post 以获取完整说明、代码示例和示例应用程序。
【讨论】:
博文好像已经过时了,现在没有例子说明如何做到这一点。以上是关于如何在颤动中为 CachedNetworkImage 编写小部件测试的主要内容,如果未能解决你的问题,请参考以下文章