Flutter Widget测试中Stub HTTP请求报错,求解决方法
Flutter Widget测试Mock服务方法失败的解决办法
问题现象
在Flutter Widget测试中,尝试为按钮点击时调用的JsonPlaceholderService().getPlaceholder()指定返回值,测试失败并抛出如下错误:
StateError (Bad state: No method stub was called from within
when().
Was a real method called, or perhaps an extension method?)
相关代码
home.dart
import 'package:flutter/material.dart'; import 'package:fluttertest/json_placeholder_service.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { String text = ''; Future<void> _onPressed() async { final response = await JsonPlaceholderService().getPlaceholder(); setState(() { text = response.title; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( child: const Text('Get text'), onPressed: () => _onPressed(), ), const SizedBox(height: 40), Text(text), ], ), ), ); } }
json_placeholder_model.dart
class JsonPlaceholderModel { int id; int userId; String title; bool completed; JsonPlaceholderModel({ required this.id, required this.userId, required this.title, required this.completed, }); Map<String, dynamic> toJson() => { 'id': id, 'userId': userId, 'title': title, 'completed': completed, }; JsonPlaceholderModel.fromJson(dynamic json) : id = json['id'], userId = json['userId'], title = json['title'], completed = json['completed']; }
json_placeholder_service.dart
import 'dart:convert'; import 'package:fluttertest/json_placeholder_model.dart'; import 'package:http/http.dart' as http; class JsonPlaceholderService { Future<JsonPlaceholderModel> getPlaceholder() async { const url = 'https://jsonplaceholder.typicode.com/todos/1'; final response = await http.get(Uri.parse(url)); final data = jsonDecode(const Utf8Decoder().convert(response.bodyBytes)); final JsonPlaceholderModel json = JsonPlaceholderModel.fromJson(data); return json; } }
widget_test.dart(原错误代码)
import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fluttertest/json_placeholder_model.dart'; import 'package:fluttertest/json_placeholder_service.dart'; import 'package:fluttertest/main.dart'; import 'package:mockito/mockito.dart'; void main() { testWidgets('Should display api text on button click', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(const MyApp()); // Stub http request final json = { "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }; final response = JsonPlaceholderModel.fromJson(json); // Methode 1 -> says to use methode 2 when(JsonPlaceholderService().getPlaceholder()) .thenReturn(Future.value(response)); // Methode 2 when(JsonPlaceholderService().getPlaceholder()) .thenAnswer((_) async => response); // Click the button final button = find.byType(ElevatedButton); expect(button, findsOneWidget); await tester.tap(button); await tester.pumpAndSettle(); await tester.pump(const Duration(seconds: 2)); // The text is displayed expect(find.text('delectus aut autem'), findsOneWidget); }); }
问题原因
Mockito只能对继承自Mock类的实例进行方法Stub,原测试中每次调用JsonPlaceholderService()都会创建新的真实类实例,导致Stub规则无法绑定到实际运行时使用的实例上,进而抛出错误。
解决方案
1. 创建Mock服务类
手动创建继承自Mock的Mock类:
class MockJsonPlaceholderService extends Mock implements JsonPlaceholderService {}
也可使用Mockito代码生成工具,添加@GenerateMocks([JsonPlaceholderService])注解后运行dart run build_runner build自动生成。
2. 改造Widget实现依赖注入
修改MyHomePage,通过构造函数注入服务实例,避免在方法内直接创建真实服务:
class MyHomePage extends StatefulWidget { const MyHomePage({ Key? key, required this.title, required this.service, // 添加服务注入参数 }) : super(key: key); final String title; final JsonPlaceholderService service; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { String text = ''; Future<void> _onPressed() async { // 使用注入的服务实例 final response = await widget.service.getPlaceholder(); setState(() { text = response.title; }); } // build方法保持不变 }
同时修改MyApp,在正式环境中传入真实服务:
class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: MyHomePage( title: 'Flutter Demo Home Page', service: JsonPlaceholderService(), // 传入真实服务 ), ); } }
3. 修改测试代码使用Mock实例
在测试中创建Mock服务实例,绑定Stub规则后注入到Widget中:
import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fluttertest/json_placeholder_model.dart'; import 'package:fluttertest/json_placeholder_service.dart'; import 'package:fluttertest/main.dart'; import 'package:mockito/mockito.dart'; // 定义Mock类 class MockJsonPlaceholderService extends Mock implements JsonPlaceholderService {} void main() { testWidgets('点击按钮后显示API返回文本', (WidgetTester tester) async { // 创建Mock服务实例 final mockService = MockJsonPlaceholderService(); // 准备模拟返回数据 final mockData = { "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }; final mockResponse = JsonPlaceholderModel.fromJson(mockData); // 绑定Stub规则:调用getPlaceholder时返回模拟数据 when(mockService.getPlaceholder()).thenAnswer((_) async => mockResponse); // 注入Mock服务并构建Widget await tester.pumpWidget(MaterialApp( home: MyHomePage(title: 'Test', service: mockService), )); // 执行按钮点击操作 await tester.tap(find.byType(ElevatedButton)); await tester.pumpAndSettle(); // 验证文本是否正确显示 expect(find.text('delectus aut autem'), findsOneWidget); }); }
4. 依赖检查
确保pubspec.yaml中已添加Mockito依赖:
dev_dependencies: flutter_test: sdk: flutter mockito: ^5.4.0
内容的提问来源于stack exchange,提问作者TheSmartMonkey
相关产品推荐
相关产品推荐

