Flutter FadeInImage组件测试:占位符与网络图切换验证求助
Flutter FadeInImage 组件测试用例实现
1. 解决"provide your own HttpClient implementation"错误
Flutter测试环境默认禁止真实网络请求,可通过HttpOverrides提供自定义HttpClient来Mock网络响应,避免触发该错误:
class MockHttpClient extends HttpClient { @override Future<HttpClientRequest> getUrl(Uri url) async { final request = await super.getUrl(url); request.headers.set('content-type', 'image/png'); return MockHttpClientRequest(request, kTransparentImage); } } class MockHttpClientRequest extends HttpClientRequest { final HttpClientRequest _realRequest; final Uint8List _mockBytes; MockHttpClientRequest(this._realRequest, this._mockBytes); @override Future<HttpClientResponse> close() async { return MockHttpClientResponse(_mockBytes); } @override dynamic noSuchMethod(Invocation invocation) => _realRequest.noSuchMethod(invocation); } class MockHttpClientResponse extends HttpClientResponse { final Uint8List _bytes; MockHttpClientResponse(this._bytes); @override int get statusCode => 200; @override Stream<List<int>> listen(void Function(List<int>)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError}) { return Stream.value(_bytes); } @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } class _MockHttpOverrides extends HttpOverrides { @override HttpClient createHttpClient(SecurityContext? context) { return MockHttpClient(); } }
在测试前注册Mock客户端:
setUp(() { HttpOverrides.global = _MockHttpOverrides(); });
2. 验证初始状态:占位符显示、网络图未加载
测试初始渲染时,kTransparentImage占位符存在,网络图片未显示:
await tester.pumpWidget( MaterialApp( home: Scaffold( body: FadeInImage.memoryNetwork( placeholder: kTransparentImage, image: 'https://example.com/test.png', ), ), ), ); // 验证占位符存在 expect(find.byWidgetPredicate((widget) => widget is Image && widget.image is MemoryImage && (widget.image as MemoryImage).bytes == kTransparentImage ), findsOneWidget); // 验证网络图未加载 expect(find.byWidgetPredicate((widget) => widget is Image && widget.image is NetworkImage && (widget.image as NetworkImage).url == 'https://example.com/test.png' ), findsNothing);
3. 等待网络图加载完成
FadeInImage默认淡入延迟为700ms,需等待足够时间完成加载和动画:
await tester.pump(const Duration(milliseconds: 700)); await tester.pumpAndSettle(); // 等待淡入动画结束
4. 验证网络图显示、占位符消失
等待完成后,验证占位符消失、网络图片成功显示:
// 验证占位符已消失 expect(find.byWidgetPredicate((widget) => widget is Image && widget.image is MemoryImage && (widget.image as MemoryImage).bytes == kTransparentImage ), findsNothing); // 验证网络图已显示 expect(find.byWidgetPredicate((widget) => widget is Image && widget.image is NetworkImage && (widget.image as NetworkImage).url == 'https://example.com/test.png' ), findsOneWidget);
完整测试代码整合
将上述逻辑整合为完整测试文件,确保导入必要依赖:
import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_test/flutter_test.dart'; class MockHttpClient extends HttpClient { @override Future<HttpClientRequest> getUrl(Uri url) async { final request = await super.getUrl(url); request.headers.set('content-type', 'image/png'); return MockHttpClientRequest(request, kTransparentImage); } } class MockHttpClientRequest extends HttpClientRequest { final HttpClientRequest _realRequest; final Uint8List _mockBytes; MockHttpClientRequest(this._realRequest, this._mockBytes); @override Future<HttpClientResponse> close() async { return MockHttpClientResponse(_mockBytes); } @override dynamic noSuchMethod(Invocation invocation) => _realRequest.noSuchMethod(invocation); } class MockHttpClientResponse extends HttpClientResponse { final Uint8List _bytes; MockHttpClientResponse(this._bytes); @override int get statusCode => 200; @override Stream<List<int>> listen(void Function(List<int>)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError}) { return Stream.value(_bytes); } @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } class _MockHttpOverrides extends HttpOverrides { @override HttpClient createHttpClient(SecurityContext? context) { return MockHttpClient(); } } void main() { setUp(() { HttpOverrides.global = _MockHttpOverrides(); }); testWidgets('FadeInImage switches from placeholder to network image', (tester) async { const testImageUrl = 'https://example.com/test.png'; // 初始渲染 await tester.pumpWidget( MaterialApp( home: Scaffold( body: FadeInImage.memoryNetwork( placeholder: kTransparentImage, image: testImageUrl, ), ), ), ); // 验证初始状态 expect(find.byWidgetPredicate((widget) => widget is Image && widget.image is MemoryImage && (widget.image as MemoryImage).bytes == kTransparentImage ), findsOneWidget); expect(find.byWidgetPredicate((widget) => widget is Image && widget.image is NetworkImage && (widget.image as NetworkImage).url == testImageUrl ), findsNothing); // 等待加载与动画 await tester.pump(const Duration(milliseconds: 700)); await tester.pumpAndSettle(); // 验证最终状态 expect(find.byWidgetPredicate((widget) => widget is Image && widget.image is MemoryImage && (widget.image as MemoryImage).bytes == kTransparentImage ), findsNothing); expect(find.byWidgetPredicate((widget) => widget is Image && widget.image is NetworkImage && (widget.image as NetworkImage).url == testImageUrl ), findsOneWidget); }); }
内容的提问来源于stack exchange,提问作者anasqadrei
相关产品推荐
相关产品推荐

