Val*_*nal 5 testing mocking dart flutter flutter-test
我正在尝试为我的 Flutter 应用程序创建测试。简单的例子:
class MyWidget extends StatelessWidget {
@override
build(BuildContext context) {
return MySecondWidget();
}
}
Run Code Online (Sandbox Code Playgroud)
我想验证它MyWidget实际上是在MySecondWidget没有构建的情况下调用MySecondWidget。
void main() {
testWidgets('It should call MySecondWidget', (WidgetTester tester) async {
await tester.pumpWidget(MyWidget());
expect(find.byType(MySecondWidget), findsOneWidget);
}
}
Run Code Online (Sandbox Code Playgroud)
在我的情况下,这将不起作用,因为MySecondWidget需要一些特定且复杂的设置(例如 API 密钥、...中的值Provider)。我想要的是“模拟” MySecondWidget为空Container(例如),以便在测试期间不会引发任何错误。
我怎么能做这样的事情?
没有任何开箱即用的方法可以模拟小部件。我将写一些关于如何在测试期间“模拟”/替换小部件的示例/想法(例如使用SizedBox.shrink().
但首先,让我解释一下为什么我认为这不是一个好主意。
在 Flutter 中,您正在构建一个 widget 树。一个特定的小部件有一个父部件,并且通常有一个或多个子部件。
出于性能原因,Flutter 选择了单通道布局算法(请参阅此):
Flutter 每帧执行一次布局,并且布局算法在单遍中工作。父对象调用其每个子对象的布局方法,将约束沿着树向下传递。子级递归地执行自己的布局,然后通过从其布局方法返回将几何图形返回到树上。重要的是,一旦渲染对象从其布局方法返回,在下一帧的布局之前将不会再次访问该渲染对象。这种方法将原本可能是单独的测量和布局传递合并到单个传递中,因此,每个渲染对象在布局期间最多被访问两次:一次在树下的路径上,一次在树上的路径上。
由此,我们需要了解父级需要其子级进行构建以获得其大小,然后正确渲染自身。如果删除它的子项,它的行为可能会完全不同。
如果可能的话,最好模拟服务。例如,如果您的孩子发出 HTTP 请求,您可以模拟 HTTP 客户端:
HttpOverrides.runZoned(() {
// Operations will use MyHttpClient instead of the real HttpClient
// implementation whenever HttpClient is used.
}, createHttpClient: (SecurityContext? c) => MyHttpClient(c));
Run Code Online (Sandbox Code Playgroud)
如果孩子需要特定的提供者,您可以提供一个虚拟的提供者:
testWidgets('My test', (tester) async {
tester.pumpWidget(
Provider<MyProvider>(
create: (_) => MyDummyProvider(),
child: MyWidget(),
),
);
});
Run Code Online (Sandbox Code Playgroud)
如果您仍然想在测试期间用另一个小部件更改一个小部件,这里有一些想法:
Platform.environment.containsKey('FLUTTER_TEST')您可以Platform从dart:io(网络上不支持)或universal_io(网络上支持)导入。
你的构建方法可以是:
@override
Widget build(BuildContext context) {
final isTest = Platform.environment.containsKey('FLUTTER_TEST');
if (isTest) return const SizedBox.shrink();
return // Your real implementation.
}
Run Code Online (Sandbox Code Playgroud)
@visibleForTestingmockChild您可以注释仅在测试文件中可见/可用的参数(例如:):
class MyWidget extends StatelessWidget {
const MyWidget({
@visibleForTesting this.mockChild,
});
final Widget? child;
@override
Widget build(BuildContext context) {
return mockChild ?? // Your real widget implementation here.
}
}
Run Code Online (Sandbox Code Playgroud)
在你的测试中:
tester.pumpWidget(
MyWidget(
mockChild: MyMockChild(),
),
);
Run Code Online (Sandbox Code Playgroud)