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.)