Flutter 中的单元测试通过 BuildContext

Nic*_*las 28 unit-testing dart flutter

我在 Dart 类中有一个方法,它接受BuildContext参数,如下所示:

class MyClass {

  <return_type> myMethodName(BuildContext context, ...) {
        ...
        doSomething
        return something;
    }
}
Run Code Online (Sandbox Code Playgroud)

我想测试该方法是否按预期工作:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
...

void main() {
  MyClass sut;

  setUp(() {
    sut = MyClass();
  });

  test('me testing', () {

    var actual = sut.myMethodName(...);        

    expect(actual, something);
  });
}
Run Code Online (Sandbox Code Playgroud)

当然不行,因为方法myMethodName需要参数BuildContext类型。该值在整个应用程序本身中都可用,但不确定在我的单元测试中从何处获取该值。

jam*_*lin 34

一种方法是testWidgetsBuilder小部件结合使用:

testWidgets('me testing', (WidgetTester tester) async {
  await tester.pumpWidget(
    Builder(
      builder: (BuildContext context) {
        var actual = sut.myMethodName(context, ...);
        expect(actual, something);

        // The builder function must return a widget.
        return Placeholder();
      },
    ),
  );
});
Run Code Online (Sandbox Code Playgroud)


sur*_*rga 17

您实际上可以模拟 ,BuildContext因此测试将无头运行。我认为它更好,但可能不是您正在寻找的解决方案。

BuildContext是一个抽象类,因此它不能被实例化。任何抽象类都可以通过创建该类的实现来模拟。如果我以你的例子为例,那么代码将如下所示:

class MockBuildContext extends Mock implements BuildContext {}

void main() {
   MyClass sut;
   MockBuildContext _mockContext;

   setUp(() {
     sut = MyClass();
     _mockContext = MockBuildContext();
   });

   test('me testing', () {

   var actual = sut.myMethodName(_mockContext, ...);        

   expect(actual, something);
  });
}
Run Code Online (Sandbox Code Playgroud)

  • 返回`参数类型'MockBuildContext'不能分配给参数类型'BuildContext'` (3认同)
  • 将人们指向可以在 [here — pub.dev](https://pub.dev/packages/mockito) 中找到的 `mockito` 包可能是一个不错的选择。这样,遇到这个答案的人就会知道他们**首先**希望安装 `mockito` 包,以便他们可以访问 `Mock` 类。谢谢 :) (2认同)

Ber*_*Ber 6

这是在测试用例中检索 BuildContext 实例的一种简单方法:

testWidgets('showDialog', (WidgetTester tester) async {
  await tester.pumpWidget(MaterialApp(home: Material(child: Container())));
  final BuildContext context = tester.element(find.byType(Container));

  final dialog = showDialog(
    context: context,
    builder: (context) => AlertDialog(
      content: Text('shown by showDialog'),
    ),
  );

  // apply your tests to dialog or its contents here.
});
Run Code Online (Sandbox Code Playgroud)

这是受到函数Flutter 测试用例中的Simple dialog control test 的启发showDialog()

整个“应用程序”由Container一个MaterialApp框架中的小部件组成。该BuildContext实例通过查找检索形式Element相关的实例Container


Abd*_*tem 6

我完全同意“surga”的答案,但在某些情况下,它还不够好。就像当你想使用它时,BuildContext例如InhiretedWidgetProviderMediaQuery

所以我建议使用默认生成器为您Mockito生成类。BuildContext

@GenerateMocks([BuildContext])
BuildContext _createContext(){
final context = MockBuildContext();
...
Run Code Online (Sandbox Code Playgroud)

并添加build_runner到您的pubspec.yaml

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: any //use any version you want
Run Code Online (Sandbox Code Playgroud)

然后运行这个命令:

flutter packages pub run build_runner build

context现在您可以从该类创建一个对象MockBuildContext,就像通常从MaterialApp.

@GenerateMocks([BuildContext])
BuildContext _createContext(){
  final context = MockBuildContext();
  final mediaQuery = MediaQuery(
    data: MediaQueryData(),
    child: const SizedBox(),
  );
  when(context.widget).thenReturn(const SizedBox());
  when(context.findAncestorWidgetOfExactType()).thenReturn(mediaQuery);
  when(context.dependOnInheritedWidgetOfExactType<MediaQuery>())
      .thenReturn(mediaQuery);
  when(context.getElementForInheritedWidgetOfExactType())
      .thenReturn(InheritedElement(mediaQuery));
 
  return context;
}

Run Code Online (Sandbox Code Playgroud)

注意:这个Mock不需要添加when..thenReturn,这取决于您的需要。