Flutter测试报错:type 'Null' is not a subtype of type 'Future<AppColors>'
我正在学习Provider,想测试从SharedPreferences加载颜色数据的逻辑,但运行测试时报错,试过Stack Overflow和AI工具都没解决。我的应用是调色板生成器,有负责加载更新颜色的Provider类,还有处理SharedPreferences存储的ThemeRepository。
报错信息
type 'Null' is not a subtype of type 'Future<AppColors>' test/theme_provider_test.dart 7:7 MockThemeRepository.loadThemeColors test/theme_provider_test.dart 18:24 main
相关代码
测试代码
class MockThemeRepository extends Mock implements ThemeRepository {} void main() { WidgetsFlutterBinding.ensureInitialized(); final themeRepository = MockThemeRepository(); when(themeRepository.loadThemeColors()).thenAnswer((_) async => AppColors( primary: Colors.blue, accent: Colors.orange, background: Colors.white, backgroundDark: Colors.grey, error: Colors.red, )); test('Test if the theme colors are loaded from SharedPreferences', () async { final loadedColors = await themeRepository.loadThemeColors(); expect(loadedColors.primary, equals(Colors.blue)); expect(loadedColors.accent, equals(Colors.orange)); expect(loadedColors.background, equals(Colors.white)); expect(loadedColors.backgroundDark, equals(Colors.grey)); expect(loadedColors.error, equals(Colors.red)); });
仓库代码
class ThemeRepository { static const String themeColorsKey = 'themeColors'; final _logger = Logger(); Future<AppColors> loadThemeColors() async { try { final prefs = await SharedPreferences.getInstance(); final encodedData = prefs.getString(themeColorsKey); if (encodedData != null) { final colorsData = json.decode(encodedData); return AppColors.fromJson(colorsData); } } catch (error, stackTrace) { _logger.e('Falha ao carregar as cores do tema', error, stackTrace); } // Retorna cores padrão em caso de falha ou ausência de cores salvas return AppColors.defaultColors(); } }
Provider代码
ThemeProvider( this.themeData, this.currentThemeColors, ); Future<void> loadThemeColors() async { try { final appColors = await repository.loadThemeColors(); updateThemeFromColors(appColors); } catch (error, stackTrace) { _logger.e('Falha ao carregar as cores do tema', error, stackTrace); } } void updateThemeFromColors(AppColors colors) { final newTheme = themeData.copyWith( colorScheme: themeData.colorScheme.copyWith( primary: colors.primary, secondary: colors.accent, background: colors.background, error: colors.error, ), primaryColor: colors.primary, appBarTheme: themeData.appBarTheme.copyWith( backgroundColor: colors.backgroundDark, ), floatingActionButtonTheme: themeData.floatingActionButtonTheme.copyWith( backgroundColor: MaterialStateColor.resolveWith((states) { if (states.contains(MaterialState.disabled)) { return colors.accent; } return colors.accent; }), ), scaffoldBackgroundColor: colors.background, ); themeData = newTheme; currentThemeColors = colors; repository.saveThemeColors(colors); notifyListeners(); }
问题
请问如何正确测试SharedPreferences的调用并使用when方法?
解决方案
错误根源
你的mock方法没有正确生效,调用loadThemeColors()时返回了null,但方法声明要求返回Future<AppColors>,导致类型不匹配报错。大概率是mock库的方法匹配逻辑出了问题,或者测试代码的初始化步骤有误。
修复步骤与示例
1. 修正Mock仓库的测试代码
确保mock方法的签名完全匹配,并且用setUp初始化测试环境,避免重复代码:
import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:your_app/models/app_colors.dart'; import 'package:your_app/repositories/theme_repository.dart'; import 'package:flutter/material.dart'; class MockThemeRepository extends Mock implements ThemeRepository {} void main() { late MockThemeRepository themeRepository; setUp(() { WidgetsFlutterBinding.ensureInitialized(); themeRepository = MockThemeRepository(); }); test('测试从Mock仓库加载主题颜色', () async { // 明确指定mock方法的返回值,确保是Future<AppColors>类型 when(themeRepository.loadThemeColors()).thenAnswer((_) async => AppColors( primary: Colors.blue, accent: Colors.orange, background: Colors.white, backgroundDark: Colors.grey, error: Colors.red, )); final loadedColors = await themeRepository.loadThemeColors(); // 断言颜色是否匹配 expect(loadedColors.primary, Colors.blue); expect(loadedColors.accent, Colors.orange); expect(loadedColors.background, Colors.white); expect(loadedColors.backgroundDark, Colors.grey); expect(loadedColors.error, Colors.red); // 验证方法确实被调用过一次 verify(themeRepository.loadThemeColors()).called(1); }); }
2. 直接测试ThemeRepository与SharedPreferences的交互
如果要真实测试SharedPreferences的调用,不需要mock整个仓库,用官方提供的mock工具即可:
import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:your_app/models/app_colors.dart'; import 'package:your_app/repositories/theme_repository.dart'; import 'dart:convert'; void main() { setUp(() { WidgetsFlutterBinding.ensureInitialized(); // 初始化SharedPreferences的mock实例 SharedPreferences.setMockInitialValues({}); }); test('无存储数据时,仓库返回默认颜色', () async { final repository = ThemeRepository(); final colors = await repository.loadThemeColors(); expect(colors, AppColors.defaultColors()); }); test('从SharedPreferences加载已存储的颜色', () async { final prefs = await SharedPreferences.getInstance(); final testColors = AppColors( primary: Colors.blue, accent: Colors.orange, background: Colors.white, backgroundDark: Colors.grey, error: Colors.red, ); // 先往mock的SharedPreferences存入数据 await prefs.setString(ThemeRepository.themeColorsKey, json.encode(testColors.toJson())); final repository = ThemeRepository(); final loadedColors = await repository.loadThemeColors(); // 断言加载的颜色与存入的一致 expect(loadedColors.primary, testColors.primary); expect(loadedColors.accent, testColors.accent); }); }
3. 测试Provider的完整逻辑
验证Provider调用仓库加载颜色后,是否正确更新自身状态:
import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:your_app/models/app_colors.dart'; import 'package:your_app/providers/theme_provider.dart'; import 'package:your_app/repositories/theme_repository.dart'; import 'package:flutter/material.dart'; class MockThemeRepository extends Mock implements ThemeRepository {} void main() { late MockThemeRepository themeRepository; late ThemeProvider themeProvider; late ThemeData initialTheme; setUp(() { WidgetsFlutterBinding.ensureInitialized(); themeRepository = MockThemeRepository(); initialTheme = ThemeData.light(); // 初始化Provider,假设你有对应的构造参数 themeProvider = ThemeProvider(initialTheme, AppColors.defaultColors()); // 给Provider注入mock仓库 themeProvider.repository = themeRepository; }); test('Provider加载颜色后正确更新主题状态', () async { final testColors = AppColors( primary: Colors.blue, accent: Colors.orange, background: Colors.white, backgroundDark: Colors.grey, error: Colors.red, ); // mock仓库的加载方法 when(themeRepository.loadThemeColors()).thenAnswer((_) async => testColors); await themeProvider.loadThemeColors(); // 验证Provider的状态是否更新 expect(themeProvider.currentThemeColors, testColors); expect(themeProvider.themeData.primaryColor, Colors.blue); expect(themeProvider.themeData.appBarTheme.backgroundColor, Colors.grey); // 验证仓库方法被调用,并且保存方法触发 verify(themeRepository.loadThemeColors()).called(1); verify(themeRepository.saveThemeColors(testColors)).called(1); }); }
内容的提问来源于stack exchange,提问作者gise
相关产品推荐
相关产品推荐

