You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter单元测试使用Provider报错:空值检查运算符用在空值上

解决Flutter单元测试中Provider空值检查错误的问题

错误原因

你遇到的Null check operator used on a null value错误,本质是因为你使用的MockBuildContext并没有注册StudentProvider实例。Provider.of需要从上下文的祖先组件树中查找对应的Provider,但你的Mock上下文不属于任何包含StudentProvider的组件树,因此返回null,触发空值检查失败。

另外你的测试代码存在两处笔误:

  • saveStuden应为saveStudent
  • sets应为定义好的sts列表

解决方案

方案1:使用ProviderContainer(推荐,纯单元测试无需Widget树)

从provider包(版本≥6.0.0)提供的ProviderContainer可以直接管理Provider实例,无需依赖Widget上下文,更适合单元测试场景:

import 'package:provider/provider.dart';
import 'package:test/test.dart';

void main() {
  late ProviderContainer container;
  late StudentProvider studentProvider;

  setUp(() {
    // 初始化Provider容器,注册StudentProvider
    container = ProviderContainer(
      overrides: [
        ChangeNotifierProvider<StudentProvider>(
          create: (_) => StudentProvider(),
        ),
      ],
    );
    // 获取Provider实例
    studentProvider = container.read<StudentProvider>();
  });

  tearDown(() {
    // 清理容器,避免测试间状态污染
    container.dispose();
  });

  test('测试学生控制器添加新学生', () async {
    List<Students> sts = [
      Students(name: 'Barbels', age: 21, weight: 70),
      Students(name: 'Hazel', age: 31, weight: 60),
    ];
    var id = '1';

    // 修改MyController.saveStudent方法,直接接收StudentProvider实例(更易测试)
    MyController.saveStudent(studentProvider, id, sts);

    // 验证结果
    expect(studentProvider.items.length, equals(2));
  });
}

如果无法修改MyController.saveStudent的参数,你可以通过container.context获取一个绑定了Provider的上下文:

final context = container.context;
MyController.saveStudent(context, id, sts);

方案2:Widget测试(使用真实上下文)

如果必须依赖Widget上下文,可以通过testWidgets构建包含StudentProvider的组件树:

import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';

void main() {
  testWidgets('测试学生控制器添加新学生', (tester) async {
    // 构建包含StudentProvider的Widget树
    await tester.pumpWidget(
      ChangeNotifierProvider(
        create: (context) => StudentProvider(),
        child: const MaterialApp(home: Scaffold(body: SizedBox())),
      ),
    );

    List<Students> sts = [
      Students(name: 'Barbels', age: 21, weight: 70),
      Students(name: 'Hazel', age: 31, weight: 60),
    ];
    var id = '1';

    // 获取真实上下文
    final context = tester.element(find.byType(SizedBox));

    MyController.saveStudent(context, id, sts);

    // 验证Provider中的数据
    final provider = Provider.of<StudentProvider>(context, listen: false);
    expect(provider.items.length, equals(2));
  });
}

额外注意事项

  • 避免使用setUpAll初始化测试依赖,改用setUp确保每个测试都有独立的实例,防止状态污染。
  • 优先选择ProviderContainer进行单元测试,减少Widget树带来的额外复杂度。

内容的提问来源于stack exchange,提问作者Bilal Rabbi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 05:42:10