使用NetworkImage进行Flutter Widget测试

Mar*_*łek 6 dart flutter

我有一个WidgetNetworkImage(所以用硬编码的网址远)。
我想对小部件进行测试,但运行小部件测试时得到404(URL为100%有效)。
我如何自己进行NetworkImages加载或(最好是)忽略它们,以使我的测试不会因为404而失败?

Ton*_*ado 18

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

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

mockNetworkImagesFor(() => tester.pumpWidget(makeTestableWidget()));
Run Code Online (Sandbox Code Playgroud)


Iir*_*kka 13

在小部件测试中,默认的HTTP客户端已替换为始终返回400s的HTTP客户端。在flutter_markdown存储库中以及其他几个地方,都有一个有关如何执行此操作的示例。我曾经将其复制并粘贴到每个项目中,但是我做了足够多次以至于变得很无聊。

(我自己)现在有一个名为“ image_test_utils”的库。您可以使用一种provideMockedNetworkImages方法包装小部件测试,该方法将模拟的HTTP客户端替换为始终返回透明图像的HTTP客户端。这又使您的测试通过。

pubspec.yaml:

dev_dependencies:
  image_test_utils: ^1.0.0
Run Code Online (Sandbox Code Playgroud)

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.
    });
  });
}
Run Code Online (Sandbox Code Playgroud)

  • 该库不再维护。我建议不要使用它。 (2认同)

Hug*_*sos 7

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

对于每个测试:

setUpAll(() => HttpOverrides.global = null);
Run Code Online (Sandbox Code Playgroud)

对于单个测试:

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


Gün*_*uer 5

我用

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

}
Run Code Online (Sandbox Code Playgroud)
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));
      }
    });
Run Code Online (Sandbox Code Playgroud)
Uint8List get dummyImageData => BASE64.decode(dummyJpgImageBase64);    
Run Code Online (Sandbox Code Playgroud)

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

const String dummyAvatarJpgImageBase64 =
'/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIi'
...   
'itf93F+MLRdehP4ZutvWj8m+rjzpz//Z';
Run Code Online (Sandbox Code Playgroud)

这样,当我从开始测试时,它也可以工作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 {}
Run Code Online (Sandbox Code Playgroud)

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,
    });
  });
Run Code Online (Sandbox Code Playgroud)

dummyAvatarImageDatadummyPngImageDatalist<int>并且包含图像数据。