目前,Flutter没有为模拟小部件提供开箱即用的解决方案。我将提供一些示例/想法,说明如何在测试期间“模拟”/替换小部件(例如使用SizedBox.shrink()
)。
但首先,让我解释一下为什么我认为这不是一个好主意。
在Flutter中,您正在构建一个小部件树形结构。特定的小部件有一个父级,通常有一个或几个子级。
Flutter出于性能原因选择了单次布局算法(请参见此文档):
Flutter每帧执行一次布局,布局算法在单次传递中运行。约束条件从父对象通过在每个子代上调用布局方法来传递到下面。子代递归执行自己的布局,然后通过从其布局方法返回来向上发送几何体。重要的是,一旦渲染对象从其布局方法返回,该渲染对象在下一帧的布局之前不会再次被访问。此方法将可能分开的测量和布局传递组合成单个传递,并且结果是,在布局期间,每个渲染对象最多只访问两次:一次在向下遍历树时,一次在向上遍历树时。
由此可知,父级需要其子级构建以获取它们的大小并正确呈现自己。如果您删除了其子级,它可能会表现完全不同。
最好模拟服务(如果可能)。例如,如果您的子级发出HTTP请求,则可以模拟HTTP客户端:
HttpOverrides.runZoned(() {
}, createHttpClient: (SecurityContext? c) => MyHttpClient(c));
如果孩子需要特定的提供者,您可以提供虚拟的提供者:
testWidgets('My test', (tester) async {
tester.pumpWidget(
Provider<MyProvider>(
create: (_) => MyDummyProvider(),
child: MyWidget(),
),
);
});
如果你想在测试期间使用其他的控件替换一个小部件,下面提供一些思路:
1. 使用 Platform.environment.containsKey('FLUTTER_TEST')
你可以从dart:io
导入Platform
(不支持Web),或者从universal_io
导入(支持Web)。
你的建造方法可能是这样的:
@override
Widget build(BuildContext context) {
final isTest = Platform.environment.containsKey('FLUTTER_TEST');
if (isTest) return const SizedBox.shrink();
return
}
您可以对仅在测试文件中可见/可用的参数(例如mockChild
)进行注释:
class MyWidget extends StatelessWidget {
const MyWidget({
@visibleForTesting this.mockChild,
});
final Widget? child;
@override
Widget build(BuildContext context) {
return mockChild ??
}
}
在你的测试中:
tester.pumpWidget(
MyWidget(
mockChild: MyMockChild(),
),
);