Unit Test -> Could not find the correct Provider<Counter> above this ChangeNotifierProvider<Counter> Widget

1,925

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));
});
Share:
1,925
Chinnon Santos
Author by

Chinnon Santos

Backend Software Developer at C6 Bank

Updated on December 15, 2022

Comments

  • Chinnon Santos
    Chinnon Santos over 1 year

    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