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