单元测试 -> 在此 ChangeNotifierProvider<Counter> 小部件上方找不到正确的 Provider<Counter>

Posted

技术标签:

【中文标题】单元测试 -> 在此 ChangeNotifierProvider<Counter> 小部件上方找不到正确的 Provider<Counter>【英文标题】:Unit Test -> Could not find the correct Provider<Counter> above this ChangeNotifierProvider<Counter> Widget 【发布时间】:2020-03-27 15:17:33 【问题描述】:

我想在我的项目中为 Provider (ChangeNotifierProvider) 创建一个单元测试,我的单元测试、小部件测试和集成测试都成功通过了✔️,所以现在我尝试了(努力尝试 ????...)为提供者创建单元测试。我能够检查上下文,但是在检查提供者的初始值(必须为 0)时,我得到了这个异常❌:

══╡ 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...

按照我的代码: - 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.darttest/widget/widget_test.darttest_driver/app_test.dart

现在不重要,但如果您想查看它,可以在 full_testing_flutter (public project) 存储库中找到它

我可以做些什么来测试提供者(隔离)?我的代码中的错误在哪里?

我从 Dart/Flutter 开始,尤其是 Provider 包,有人可以帮助我吗? ????

注意:我的应用程序运行良好,只有我对提供者的单元测试(我现在正在实施的)不起作用!!!

谢谢

【问题讨论】:

【参考方案1】:

出现此问题是因为您使用要获取的提供者的BuildContext 来调用Provider.of

Provider<T>(
  key: myKey,
  ...
)


Provider.of<T>(myKey.currentContext);

这是不可能的,只有该提供者的后代才能调用Provider.of

考虑将您的测试更改为:

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

【讨论】:

非常非常好!!!现在它完美地工作了......非常感谢我使用我自己的 MyApp 小部件和我的模型类 Counter,所以它看起来像这样:final BuildContext childContext = tester.element(find.byType(MyApp));expect(Provider.of&lt;Counter&gt;(childContext).value, 0);

以上是关于单元测试 -> 在此 ChangeNotifierProvider<Counter> 小部件上方找不到正确的 Provider<Counter>的主要内容,如果未能解决你的问题,请参考以下文章

spring单元测试

spring web中完成单元测试

构建之法阅读笔记一

如何对 PHP 特征进行单元测试

如何在单元测试 Nest.js 中测试抛出新的 HttpException

构建之法阅读笔记03