简体   繁体   English

如何在颤振中为 CachedNetworkImage 编写小部件测试

[英]How to write widget test for CachedNetworkImage in flutter

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?我可以使用 HttpOverrides 小部件测试 Image.network 并尝试使用以下代码来测试 CachedNetworkImage 没有成功 有没有人已经测试过这个包? 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?我也尝试使用 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;
}

The CachedNetworkImage widget accepts an optional cacheManager property (default to a DefaultCacheManager instance). CachedNetworkImage小部件接受一个可选的cacheManager属性(默认为DefaultCacheManager实例)。

The idea is to use get_it to inject a DefaultCacheManager instance in your app, and a custom one in the test.这个想法是使用get_it在您的应用程序中注入一个DefaultCacheManager实例,并在测试中注入一个自定义实例。 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() :应用程序main()

GetIt.instance.registerSingleton<BaseCacheManager>(
  DefaultCacheManager(),
);

Test main() :测试main()

GetIt.instance.registerSingleton<BaseCacheManager>(
  TestCacheManager(),
);

Then you should be able to pump an instance of the MyImage widget:然后你应该能够抽取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();

See this blog post for full explanations, code sample and example app.有关完整说明、代码示例和示例应用程序,请参阅此博客文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM