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

Issue

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

Solution

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

Answered By – Rémi Rousselet

Answer Checked By – Gilberto Lyons (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.