首页 文章

Flutter Widget使用NetworkImage进行测试

提问于
浏览
3

我有 WidgetNetworkImage (到目前为止,硬编码的网址) .
我想对这个Widget进行小部件测试,但是当我运行小部件测试时,我得到了404(url是100%有效) .
如何使 NetworkImages 自己加载或(这会更好) ignore them 以便我的测试不会因404而失败?

2 回答

  • 3

    我用

    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);
    

    (我使用http://base64.wutils.com/encoding-online/创建了图像数据Base64)

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

    这样,当我使用 flutter run -t test/image_test.dart 启动时,测试也可以工作,但图像数据也可以从图像文件中提供,以进行正常的测试运行 .

    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,
        });
      });
    

    dummyAvatarImageDatadummyPngImageDatalist<int> 并包含图像数据 .

  • 2

    在窗口小部件测试中,默认HTTP客户端has been replaced,其中一个始终返回400 . 有关如何在flutter_markdown repo以及其他几个地方执行此操作的示例 . 我曾经把它复制并粘贴到每个项目中,但是我做了足够多的时间让它变得很无聊 .

    现在有一个库(由我),名为"image_test_utils" . 您可以使用 provideMockedNetworkImages 方法包装窗口小部件测试,该方法将模拟的HTTP客户端替换为始终返回透明图像的HTTP客户端 . 这反过来使你的测试通过 .

    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.
        });
      });
    }
    

相关问题