I want to create a unit test for the Provider (ChangeNotifierProvider
) in my project, my unit test, widget test and integration tests passed successfully ✔️, so now I tried (tried hard 🥵...) to create a unit test to the provider. I was able to check the context, but when checking the initial value of the provider (must be 0), I get this exception ❌:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following ProviderNotFoundError was thrown running a test:
Error: Could not find the correct Provider<Counter> above this ChangeNotifierProvider<Counter>
Widget
To fix, please:
* Ensure the Provider<Counter> is an ancestor to this ChangeNotifierProvider<Counter> Widget
* Provide types to Provider<Counter>
* Provide types to Consumer<Counter>
* Provide types to Provider.of<Counter>()
* Always use package imports. Ex: `import 'package:my_app/my_code.dart';
* Ensure the correct `context` is being used.
If none of these solutions work, please file a bug at:
https://github.com/rrousselGit/provider/issues
When the exception was thrown, this was the stack:
#0 Provider.of (package:provider/src/provider.dart:264:7)
#1 main.<anonymous closure>.<anonymous closure> (file:///home/chinnonsantos/FlutterProjects/full_testing_flutter/test/unit/provider_test.dart:33:23)
<asynchronous suspension>
#2 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:119:25)
<asynchronous suspension>
#3 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:648:19)
<asynchronous suspension>
#6 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:631:14)
#7 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1016:24)
#13 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1013:15)
#14 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:116:22)
#15 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:168:27)
<asynchronous suspension>
#16 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:250:15)
<asynchronous suspension>
#21 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:247:5)
#22 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:166:33)
#27 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:165:13)
<asynchronous suspension>
#28 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:400:25)
<asynchronous suspension>
#42 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:382:19)
#43 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:416:5)
#44 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:172:12)
(elided 28 frames from class _FakeAsync, package dart:async, package dart:async-patch, and package stack_trace)
The test description was:
Update when the value changes
════════════════════════════════════════════════════════════════════════════════════════════════════
00:03 +0 -1: [Provider] Update when the value changes [E]
Test failed. See exception logs above.
The test description was: Update when the value changes
00:03 +0 -1: Some tests failed.
Collecting coverage information...
Follow my code:
- pubspec.yaml
:
...
dependencies:
flutter:
sdk: flutter
test: ^1.6.3
provider: ^3.2.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_driver:
sdk: flutter
pedantic: ^1.8.0+1
...
lib/main.dart
:import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:full_testing_flutter/counter.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({this.title});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Consumer<Counter>(
builder: (context, counter, child) => Text(
'${counter.value}',
key: Key('counter'),
style: Theme.of(context).textTheme.display1,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
key: Key('increment'),
onPressed: () =>
Provider.of<Counter>(context, listen: false).increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
lib/counter.dart
:import 'package:flutter/foundation.dart';
class Counter with ChangeNotifier {
int value = 0;
void increment() {
value++;
// print('Value++: $value');
notifyListeners();
}
void decrement() {
value--;
// print('Value--: $value');
notifyListeners();
}
}
test/unit/provider_test.dart
:import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:full_testing_flutter/main.dart';
import 'package:full_testing_flutter/counter.dart';
void main() async {
Counter _counterModel;
setUp(() {
_counterModel = Counter();
});
group('[Provider]', () {
testWidgets('Update when the value changes', (tester) async {
final _providerKey = GlobalKey();
BuildContext context;
await tester.pumpWidget(ChangeNotifierProvider<Counter>(
key: _providerKey,
create: (c) {
context = c;
return Counter();
},
child: MyApp(),
));
// Check the context test...
expect(context, equals(_providerKey.currentContext));
// Check the initial value provider 0...
expect(Provider.of<Counter>(_providerKey.currentContext).value, 0);
// // Increment value...
// Provider.of<Counter>(_providerKey.currentContext).increment();
// // Delay the pump...
// await Future.microtask(tester.pump);
// // Check if incremented value is the same as received...
// expect(
// Consumer<Counter>(
// builder: (context, counter, child) => Text('${counter.value}'),
// ),
// _counterModel.value,
// );
// // Decrement value...
// Provider.of<Counter>(context, listen: false).decrement();
// // Delay the pump...
// await Future.microtask(tester.pump);
// // Check if decremented value is the same as received...
// expect(
// Provider.of<Counter>(_childKey.currentContext).value,
// _counterModel.value,
// );
});
});
}
test/unit/counter_test.dart
, test/widget/widget_test.dart
and test_driver/app_test.dart
:
It's not important right now, but if you want to see it, it's available in the full_testing_flutter (public project) repository
What I can do to test the Provider (isolated)? Where is the error in my code?
I'm starting with Dart/Flutter and especially the Provider package, can anyone help me? 😥
Note: My app works perfectly, only my unit test for the provider (what I'm implementing now) doesn't work !!!
Thanks
This problem happens because you are using the BuildContext
of the provider you want to obtain to call Provider.of
:
Provider<T>(
key: myKey,
...
)
Provider.of<T>(myKey.currentContext);
This is not possible, and only the descendants of the said provider can call Provider.of
.
Consider changing your test to something like:
testWidget('Provider.of', (tester) async {
await tester.pumpWidget(
Provider(
create: (_) => 42,
child: Container(),
),
);
final context = tester.element(find.byType(Container));
expect(Provider.of<int>(context), equals(42));
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With