Flutter Widget使用NetworkImage进行测试

时间:2018-03-08 05:44:55

标签: dart flutter

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

4 个答案:

答案 0 :(得分:8)

在小部件测试中,默认的HTTP客户端has been replaced始终返回400s。 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.
    });
  });
}

答案 1 :(得分: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启动时,测试也会起作用,但图像数据也可以从图像文件中提供,以进行正常的测试运行。

使用mockito

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 :(得分:2)

几年后,现在 image_test_utils 包似乎不再维护,这里有另一个简单的解决方案。

我使用了 network_image_mock 包(支持 nullsafety)并在我的测试中只添加了两行代码。像这样用 pumpWidget 包裹您的 mockNetworkImagesFor 调用,您将不会再收到图像加载错误:

mockNetworkImagesFor(() => tester.pumpWidget(makeTestableWidget()));

答案 3 :(得分:1)

如果您遇到这种非常不寻常的情况,即小部件测试全部是关于图像是否正确获取的,则可以撤消覆盖。

对于每次测试:

setUpAll(() => HttpOverrides.global = null);

对于单个测试:

testWidgets('Image get correctly fetched.', () {
  HttpOverrides.runZoned(
    // Run your tests.
    () {},
    createHttpClient: (securityContext) => MockHttpClient(securityContext),
  );
});