【问题标题】:Unit testing in Flutter passing BuildContextFlutter 中的单元测试通过 BuildContext
【发布时间】:2019-10-10 03:52:21
【问题描述】:

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

class MyClass {

  <return_type> myMethodName(BuildContext context, ...) {
        ...
        doSomething
        return something;
    }
}

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

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

当然不行,因为方法myMethodName需要一个参数BuildContext类型。这个值在整个应用程序本身中都是可用的,但不确定在我的单元测试中从哪里得到它。

【问题讨论】:

    标签: unit-testing dart flutter


    【解决方案1】:

    一种方法是将testWidgetsBuilder widget 结合使用:

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

    【讨论】:

    • 啊,漂亮。我测试了它,但有一个例外:A build function returned null...。似乎需要返回任何类型的小部件。我刚刚添加了return Container() 并且似乎以这种方式工作
    • @Nicolas 哎呀!对不起,我忘记了。我已经更正了。
    【解决方案2】:

    您实际上可以模拟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);
      });
    }
    

    【讨论】:

    • 将人们指向mockito 包可能是一个不错的选择,该包可以在here — pub.dev 中找到。这样,遇到此答案的人会知道他们首先希望安装mockito 包,以便他们可以访问Mock 类。谢谢你:)
    【解决方案3】:

    这是在测试用例中检索 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.
    });
    

    这是受 Simple dialog control test 的启发,来自 showDialog() 函数的 Flutter 测试用例。

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

    【讨论】:

    • 最佳答案,因为它允许我点击一个按钮,然后期待在 UI 重建后作为按钮点击的结果。使用构建器不允许我这样做。
    【解决方案4】:

    我对“surga”的回答完全满意,但在某些情况下,这还不够好。 比如当你想将这个BuildContextInhiretedWidget 一起使用时,例如:ProviderMediaQuery

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

    @GenerateMocks([BuildContext])
    BuildContext _createContext(){
    final context = MockBuildContext();
    ...
    

    并将build_runner 添加到您的pubspec.yaml

    dev_dependencies:
      flutter_test:
        sdk: flutter
      build_runner: any //use any version you want
    

    然后运行这个命令:

    flutter packages pub run build_runner build

    现在您可以从MockBuildContext 类创建context 对象,因为它通常是从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;
    }
    
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-04
      • 2023-01-30
      • 2021-10-06
      • 1970-01-01
      • 1970-01-01
      • 2017-08-15
      • 2021-03-13
      • 2020-05-01
      相关资源
      最近更新 更多