Unit testing in Flutter passing BuildContext

I am totally fine with 'surga' answer, but in some cases, it won't be good enough. like when you want to use this BuildContext with InhiretedWidget for example: Provider or MediaQuery.

So I suggest using the Mockito default generator to generate the BuildContext class for you.

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

And add build_runner to your 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.

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

Note: It's not required to add when..thenReturn's for this Mock, it is depends on your needs.


Here is a simple way to retrieve a BuildContext instance inside a test case:

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.

The whole "app" consist of a Container widget in a MaterialApp frame. The BuildContext instance is retrieved form by finding the Element instance related to the Container.


One way is to use testWidgets in combination with a Builder 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();
      },
    ),
  );
});

You can actually mock the BuildContext so the test will run headless. 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. 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);
  });
}

(Note that this requires the Mockito package: https://pub.dev/packages/mockito.)