Gur*_*ngh 8 http dart flutter widget-test-flutter
我正在尝试为屏幕而不是主应用程序编写小部件测试。这是我第一次编写小部件测试,我找不到该问题的正确解决方案。我不知道如何为此编写适当的测试。我尝试编写一个简单的小部件测试,但最终给出了如下错误“警告:此套件中至少有一个测试创建了一个 HttpClient。当运行使用 TestWidgetsFlutterBinding 的测试套件时,所有 HTTP 请求都将返回状态代码 400,并且实际上不会发出任何网络请求。任何期望真实网络连接和状态代码的测试都将失败。要测试需要 HttpClient 的代码,请向被测代码提供您自己的 HttpClient 实现,以便您的测试能够一致地提供可测试的对被测代码的响应。” 我刚刚开始学习,请帮助我。注意:我的测试只是编写一个用于查找文本小部件的基本测试。
class BookingDetails extends StatefulWidget {
final booking;
BookingDetails(this.booking);
@override
_BookingDetailsState createState() => _BookingDetailsState();
}
class _BookingDetailsState extends State<BookingDetails>
with AutomaticKeepAliveClientMixin {
Row _buildTeacherInfo(Map<String, dynamic> teacherData) {
return teacherData != null
? Row(
children: <Widget>[
CircleAvatar(
radius: 53,
backgroundColor: MyColors.primary,
child: CircleAvatar(
radius: 50.0,
backgroundImage: teacherData['user']['img_url'] == null ||
teacherData['user']['img_url'] == ''
? AssetImage('assets/images/placeholder_avatar.png')
: NetworkImage(teacherData['user']['img_url']),
backgroundColor: Colors.transparent,
),
),
SizedBox(width: 20.0),
Column(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Text(
'${teacherData['user']['first_name']} ',
style: AppStyles.textHeader1Style,
),
Text(
'${teacherData['user']['last_name']}',
style: AppStyles.textHeader1Style,
),
],
),
),
ElevatedButton(
onPressed: () {
//View Profile method
},
style: ElevatedButton.styleFrom(
primary: MyColors.primary,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(25))),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.next_plan_outlined),
SizedBox(width: 10.0),
Text('VIEW PROFILE'),
],
),
),
],
),
],
)
: Row(
children: <Widget>[
CircleAvatar(
radius: 48,
backgroundColor: MyColors.primary,
child: CircleAvatar(
radius: 45.0,
backgroundImage:
AssetImage('assets/images/placeholder_avatar.png'),
backgroundColor: Colors.transparent,
),
),
SizedBox(width: 20.0),
Expanded(
child: Text(
'Teacher allocation in progress',
style: AppStyles.textHeader1Style,
),
)
],
);
}
Widget _buildBookingDetails(
Map<String, dynamic> booking,
List<dynamic> campusData, // one campus' data is an array for some reason.
Map<String, dynamic> instData,
) {
return Expanded(
child: Scrollbar(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.location_on),
title: Text(
'${campusData[0]['address_line1']},'
' ${campusData[0]['suburb']}, '
'${campusData[0]['state']} ${campusData[0]['postcode']} ',
style: AppStyles.textHeader3Style,
),
),
}
@override
Widget build(BuildContext context) {
super.build(context);
return FutureBuilder(
future: Future.wait([_teacherData, _campusData, _classData, _instData]),
builder: (context, snapshot) => snapshot.connectionState ==
ConnectionState.waiting
? MyLoadingScreen(message: 'Loading booking data, please wait...')
: snapshot.hasData
? SafeArea(
child: Container(
margin: const EdgeInsets.only(top: 30.0),
child: Padding(
padding: const EdgeInsets.all(30),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_buildTeacherInfo(snapshot.data[0]),
Divider(color: MyColors.dividerColor),
SizedBox(height: 10),
const SizedBox(height: 10),
Divider(
color: MyColors.primary,
thickness: 1,
),
const SizedBox(height: 10),
_buildBookingDetails(
widget.booking,
snapshot.data[1],
snapshot.data[3],
),
SizedBox(height: 10),
Divider(
color: MyColors.primary,
thickness: 1,
),
SizedBox(height: 10),
Center(
child: widget.booking['cancelled_by_inst'] == true
? Text(
'Canceled',
style: AppStyles.textHeader3StyleBold,
)
: widget.booking['teacher_id'] == null
? Center(
child: Text(
'Teacher Allocation in Progress',
style: AppStyles.textHeader3StyleBold,
),
)
: null,
),
}
Run Code Online (Sandbox Code Playgroud)
Jan*_*nux 10
我已将您的代码减少到以下最小版本,以便能够执行它:
snippet.dart:
import 'package:flutter/material.dart';
import 'dart:convert';
import 'api.dart';
class BookingDetails extends StatefulWidget {
final Map<String, String> booking;
BookingDetails(this.booking);
@override
_BookingDetailsState createState() => _BookingDetailsState();
}
class _BookingDetailsState extends State<BookingDetails> {
late Future _campusData;
Future<dynamic> _fetchCampusData() async {
var campusID = widget.booking['campus_id'];
if (campusID != null) {
var response = await api.getCampusByID(campusID);
return json.decode(response.body);
}
}
@override
void initState() {
_campusData = _fetchCampusData();
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _campusData,
builder: (context, snapshot) {
if (snapshot.hasData) {
return const Text('Displaying data');
} else if (snapshot.hasError) {
return const Text('An error occurred.');
} else {
return const Text('Loading...');
}
}
);
}
}
Run Code Online (Sandbox Code Playgroud)
api.dart:
import 'package:http/http.dart' as http;
final _ApiClient api = _ApiClient();
class _ApiClient {
Future<http.Response> getCampusByID(String id) async {
var url = Uri.parse('https://run.mocky.io/v3/49c23ebc-c107-4dae-b1c6-5d325b8f8b58');
var response = await http.get(url);
if (response.statusCode >= 400) {
throw "An error occurred";
}
return response;
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个小部件测试,它重现了您所描述的错误:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:widget_test/snippet.dart';
void main() {
testWidgets('Should test widget with http call', (WidgetTester tester) async {
var booking = <String, String>{
'campus_id': '2f4fccd2-e199-4989-bad3-d8c48e66a15e'
};
await tester.pumpWidget(TestApp(BookingDetails(booking)));
expect(find.text('Loading...'), findsOneWidget);
await tester.pump();
expect(find.text('Displaying data'), findsOneWidget);
});
}
class TestApp extends StatelessWidget {
final Widget child;
TestApp(this.child);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: child,
);
}
}
Run Code Online (Sandbox Code Playgroud)
以下是错误信息,供参考:
Test failed. See exception logs above.
The test description was: Should test widget with http call
Warning: At least one test in this suite creates an HttpClient. When
running a test suite that uses TestWidgetsFlutterBinding, all HTTP
requests will return status code 400, and no network request will
actually be made. Any test expecting a real network connection and
status code will fail.
To test code that needs an HttpClient, provide your own HttpClient
implementation to the code under test, so that your test can
consistently provide a testable response to the code under test.
Run Code Online (Sandbox Code Playgroud)
该错误告诉您问题所在:您不得在小部件测试中执行 HTTP 调用。因此,您需要模拟该 HTTP 调用,以便调用模拟而不是真正的 HTTP 调用。有很多选项可以做到这一点,例如使用mockito包。
这里有一个可能的解决方案,使用nock包来模拟 HTTP 级别的 HTTP 响应。
pubspec.yaml:
Test failed. See exception logs above.
The test description was: Should test widget with http call
Warning: At least one test in this suite creates an HttpClient. When
running a test suite that uses TestWidgetsFlutterBinding, all HTTP
requests will return status code 400, and no network request will
actually be made. Any test expecting a real network connection and
status code will fail.
To test code that needs an HttpClient, provide your own HttpClient
implementation to the code under test, so that your test can
consistently provide a testable response to the code under test.
Run Code Online (Sandbox Code Playgroud)
小部件测试:
dev_dependencies:
nock: ^1.1.2
Run Code Online (Sandbox Code Playgroud)