简体   繁体   English

Flutter 中的单元测试通过 BuildContext

[英]Unit testing in Flutter passing BuildContext

I have a method in a Dart class, which accepts BuildContext parameter, as follows:我在 Dart 类中有一个方法,它接受BuildContext参数,如下所示:

class MyClass {

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

I want to test that the method works as expected:我想测试该方法是否按预期工作:

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

Of course, it won't work, because the method myMethodName needs a parameter BuildContext type.当然不行,因为方法myMethodName需要一个参数BuildContext类型。 This value is available throughout the application itself, but not sure where to get that from in my unit tests.该值在整个应用程序本身中都可用,但不确定在我的单元测试中从何处获取该值。

One way is to use testWidgets in combination with a Builder widget :一种方法是将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();
      },
    ),
  );
});

You can actually mock the BuildContext so the test will run headless.您实际上可以模拟BuildContext以便测试将无头运行。 I think it's better but might be not a solution that you are looking for.我认为它更好,但可能不是您正在寻找的解决方案。

BuildContext is an abstract class therefore it cannot be instantiated. BuildContext是一个抽象类,因此它不能被实例化。 Any abstract class can be mocked by creating implementations of that class.任何抽象类都可以通过创建该类的实现来模拟。 If I take your example then the code will look like this:如果我以你的例子为例,那么代码将如下所示:

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

Here is a simple way to retrieve a BuildContext instance inside a test case:这是在测试用例中检索 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.
});

This was inspired by Simple dialog control test from the Flutter test cases for the showDialog() function.这是受 Flutter 测试用例中showDialog()函数的Simple dialog control test 的启发。

The whole "app" consist of a Container widget in a MaterialApp frame.整个“应用程序”由MaterialApp框架中的Container小部件组成。 The BuildContext instance is retrieved form by finding the Element instance related to the Container . BuildContext实例是通过查找与Container相关的Element实例来检索的。

I am totally fine with 'surga' answer, but in some cases, it won't be good enough.我对“surga”的回答完全没问题,但在某些情况下,这还不够好。 like when you want to use this BuildContext with InhiretedWidget for example: Provider or MediaQuery .就像当您想将此BuildContextInhiretedWidget一起使用时,例如: ProviderMediaQuery

So I suggest using the Mockito default generator to generate the BuildContext class for you.所以我建议使用Mockito默认生成器为你生成BuildContext类。

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

And add build_runner to your pubspec.yaml并将build_runner添加到您的pubspec.yaml

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

Then run this command:然后运行这个命令:

flutter packages pub run build_runner build

Now you can create a context object from the MockBuildContext class as it is created normally from the MaterialApp .现在您可以从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);
  return context;
}

Note: It's not required to add when..thenReturn 's for this Mock, it is depends on your needs.注意:不需要为这个 Mock 添加when..thenReturn ,这取决于您的需求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM