使用 Mockito 的 FutureBuilder 快照数据为空 - Flutter
Posted
技术标签:
【中文标题】使用 Mockito 的 FutureBuilder 快照数据为空 - Flutter【英文标题】:FutureBuilder Snapshot Data is null with Mockito - Flutter 【发布时间】:2021-12-26 03:58:04 【问题描述】:我正在为我的颤振应用程序构建一些测试,并且我发现它在调用 API 的方法返回数据之前就已经在未来的构建器上,所以快照总是空的。
如何强制 FutureBuilder 等待方法返回数据?
测试代码:
@GenerateMocks([http.Client])
void main()
group('Landing Screen', ()
testWidgets('When get to server endpoint, then LandingScreen should shows ProductBox', (WidgetTester tester) async
await tester.runAsync(() async
final client = MockClient();
final product = Product(id: "a1838sn18f", name: "Sabão em pó", description:"Tests",supplierItemCode: "35", unityMeasurement: "kg", unityMeasurementQuantity: 5, image:"https://a-static.mlcdn.com.br/1500x1500/sabao-em-pedra-ype-5-unidades-ype/costaatacado/90131/fd9f6b24567bc11702c189709a9f8dd3.jpg" );
final supplier = Supplier(fantasyName: "aisdiaj", companyName: "TEste s1", CNPJ: "5575554");
final findProductBox = find.byWidget(ProductBox(productBoxModel: ProductBoxModel(product: product , supplier: supplier),));
when(client
.get(Uri.http(DOMAIN, LAST_SEARCH_URL, queryParameters)))
.thenAnswer((_) async =>
http.Response('["product":"id":"a1838sn18f","createdByUserId":"fake","createdDate":null,"lastModifiedByUserId":null,"lastModifiedDate":null,"name":"Sabão em pó","description":"Tests","supplierItemCode":"35","unityMeasurement":"kg","unityMeasurementQuantity":5,"image":"https://a-static.mlcdn.com.br/1500x1500/sabao-em-pedra-ype-5-unidades-ype/costaatacado/90131/fd9f6b24567bc11702c189709a9f8dd3.jpg"'
',"supplier":"id":"asdle2","createdByUserId":null,"createdDate":null,"lastModifiedByUserId":null,"lastModifiedDate":null,"fantasyName":"aisdiaj","companyName":"TEste s1","cnpj":"5575554"]'
, 200));
when(client
.get(Uri.http(DOMAIN, MOST_SEARCH_URL, queryParameters)))
.thenAnswer((_) async =>
http.Response('["product":"id":"a1838sn18f","createdByUserId":"fake","createdDate":null,"lastModifiedByUserId":null,"lastModifiedDate":null,"name":"Sabão em pó","description":"Tests","supplierItemCode":"35","unityMeasurement":"kg","unityMeasurementQuantity":5,"image":"https://a-static.mlcdn.com.br/1500x1500/sabao-em-pedra-ype-5-unidades-ype/costaatacado/90131/fd9f6b24567bc11702c189709a9f8dd3.jpg"'
',"supplier":"id":"asdle2","createdByUserId":null,"createdDate":null,"lastModifiedByUserId":null,"lastModifiedDate":null,"fantasyName":"aisdiaj","companyName":"TEste s1","cnpj":"5575554"]'
, 200));
await tester.pumpWidget(MaterialApp(home: LandingScreen(client),));
expect(await findProductBox, findsWidgets);
);
);
);
这是 FutureBuilder:
class LandingScreen extends StatelessWidget
http.Client client;
LandingScreen(this.client);
var lastSearch = <ProductBoxModel> [];
var mostSearch = <ProductBoxModel> [];
Color color = Colors.grey.withOpacity(0.5);
@override
Widget build(BuildContext context)
LandingScreenRoutes landingScreenRoutes = LandingScreenRoutes(client);
var lastSearchFuture = landingScreenRoutes.getLastSearch('5'); //TODO this idUser should come from logged user
var mostSearchFuture = landingScreenRoutes.getMostSearch('5'); //TODO this idUser should come from logged user
****HIDED CONTENT****
Expanded(
child: FutureBuilder(
future: lastSearchFuture,
builder: (context, snapshot)
if(snapshot.hasData)
lastSearchFuture.asStream().forEach((element) lastSearch.addAll(element););
return Padding(
padding: sidePadding,
child: Container(
width: size.width,
height: size.height,
child: ListView.builder(
itemCount: lastSearch.length ,
itemBuilder: (context, index)
return ProductBox(
productBoxModel: lastSearch[index],
);
,
),
)
);
else
return Container(
alignment: Alignment.center,
width: size.width,
height: size.height,
child: CircularProgressIndicator(),
);
)
),
这是 LandingScreenRoutes 类:
const DOMAIN = "10.0.2.2:9090";
const LAST_SEARCH_URL = "/news/lastSearch";
const MOST_SEARCH_URL = "/news/mostSearch";
class LandingScreenRoutes
http.Client client;
LandingScreenRoutes(this.client);
Future<List<ProductBoxModel>> getLastSearch(String userId) async
final queryParameters = 'userId':userId;
var response = await client.get(Uri.http(DOMAIN, LAST_SEARCH_URL, queryParameters));
List<ProductBoxModel> productList = [];
for(Map<String, dynamic> p in jsonDecode(response.body))
productList.add(ProductBoxModel.fromJson(p));
return productList;
Future<List<ProductBoxModel>> getMostSearch(String userId) async
final queryParameters = 'userId':userId;
var response = await client.get(Uri.http(DOMAIN, MOST_SEARCH_URL, queryParameters));
List<ProductBoxModel> productList = [];
for(Map<String, dynamic> p in jsonDecode(response.body))
productList.add(ProductBoxModel.fromJson(p));
return productList;
应用程序首先检查快照是否有数据:
在它进入从后端获取数据的方法之后:
这不应该发生。因此,snapshot.hasdata 总是为假,我的测试不会通过。
如何强制应用程序等到有数据或其他东西?
【问题讨论】:
【参考方案1】:你尝试过expectLater吗?
之前:
expect(await findProductBox, findsWidgets);
之后:
expectLater(await findProductBox, findsWidgets);
编辑: 我在这里复制了这个问题,试试:
@GenerateMocks([http.Client])
void main()
group('Landing Screen', ()
testWidgets('When get to server endpoint, then LandingScreen should shows ProductBox',
(WidgetTester tester) async
final client = MockClient();
when(client.get(any)).thenAnswer((_) async =>
http.Response(
'["product":"id":"a1838sn18f","createdByUserId":"fake","createdDate":null,"lastModifiedByUserId":null,"lastModifiedDate":null,"name":"Sabão em pó","description":"Tests","supplierItemCode":"35","unityMeasurement":"kg","unityMeasurementQuantity":5,"image":"https://a-static.mlcdn.com.br/1500x1500/sabao-em-pedra-ype-5-unidades-ype/costaatacado/90131/fd9f6b24567bc11702c189709a9f8dd3.jpg"'
',"supplier":"id":"asdle2","createdByUserId":null,"createdDate":null,"lastModifiedByUserId":null,"lastModifiedDate":null,"fantasyName":"aisdiaj","companyName":"TEste s1","cnpj":"5575554"]'
, 200
)
);
await tester.pumpWidget(MaterialApp(home: LandingScreen(client: client),));
await tester.pumpAndSettle();
expectLater(find.byType(ProductBox, skipOffstage: false), findsWidgets);
);
);
在屏幕上:
return Column(children: [
Expanded(
child: FutureBuilder<List<ProductBoxModel>>(
future: LandingScreenRoutes(client).getLastSearch('5'),
builder: (_, snapshot)
if (snapshot.hasData)
return Padding(
padding: const EdgeInsets.all(16),
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (_, index)
return ProductBox(
key: Key(snapshot.data![index].product.id),
productBoxModel: snapshot.data![index],
);
,
));
else
return Container(
alignment: Alignment.center,
child: const CircularProgressIndicator(),
);
))
]);
你可以使用pumpAndSettle来等待所有异步方法都被解析,并且参数skipOffstage: false具有检查屏幕边界之外的项目的功能,对于列表测试非常有用,因为有些项目可能不可见
【讨论】:
同样的结果。关键是,即使我希望以后,应用程序也会落在我调用后端的方法中,因为它会检查快照是否有数据。所以我需要强制应用程序等到快照有数据或尝试其他东西。 我用可能的解决方案编辑了答案以上是关于使用 Mockito 的 FutureBuilder 快照数据为空 - Flutter的主要内容,如果未能解决你的问题,请参考以下文章