gpt4 book ai didi

使用 HttpClient 进行 Flutter Widget 测试

转载 作者:行者123 更新时间:2023-12-02 18:32:34 28 4
gpt4 key购买 nike

我正在尝试为屏幕而不是主应用程序编写小部件测试。这是我第一次编写小部件测试,我找不到合适的解决方案。我不知道如何为此编写适当的测试。我尝试编写一个简单的小部件测试,但最终出现如下错误“警告:此套件中至少有一个测试会创建一个 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,
),
}

最佳答案

我已将您的代码缩减为以下最小版本,以便能够执行它:

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...');
}
}

);
}
}

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

这是一个小部件测试,它重现了您描述的错误:

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

错误信息如下,供引用:

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.

解决方案

该错误告诉您问题所在:您不能在小部件测试中执行 HTTP 调用。因此,您需要模拟该 HTTP 调用,以便调用模拟而不是真正的 HTTP 调用。您可以使用许多选项来做到这一点,例如使用 mockito包。

这是使用 nock 的可能解决方案在 HTTP 级别模拟 HTTP 响应的包。

pubspec.yaml:

dev_dependencies:
nock: ^1.1.2

小部件测试:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:nock/nock.dart';
import 'package:widget_test/snippet.dart';

void main() {
setUpAll(nock.init);

setUp(() {
nock.cleanAll();
});

testWidgets('Should test widget with http call', (WidgetTester tester) async {
nock('https://run.mocky.io')
.get('/v3/49c23ebc-c107-4dae-b1c6-5d325b8f8b58')
.reply(200, json.encode('{"id": "49c23ebc-c107-4dae-b1c6-5d325b8f8b58", "name": "Example campus" }'));

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

关于使用 HttpClient 进行 Flutter Widget 测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69248403/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com