目前正在尝试为我的 cubit 实施单元测试,并且感觉严重缺乏 BLoC 在线测试文档或有用的示例。谁能解释我在设置这个测试时做错了什么。我似乎无法弄清楚问题是什么。如果有人能对我能做些什么来解决这个问题有所了解,将不胜感激。
设置状态文件:
class SettingsState extends Equatable {
late final bool expandNavigation;
late final bool gpsEnabled;
late final bool celsiusEnabled;
late final bool notificationsEnabled;
late final bool breakingNewsNotifications;
late final bool trendingNotifications;
late final bool liveRemindersNotifications;
late final bool sportsNotifications;
SettingsState({
required this.notificationsEnabled,
required this.gpsEnabled,
required this.celsiusEnabled,
required this.expandNavigation,
required this.breakingNewsNotifications,
required this.trendingNotifications,
required this.liveRemindersNotifications,
required this.sportsNotifications,
});
SettingsState copyWith({
bool? gpsEnabled,
bool? celsiusEnabled,
bool? expandNavigation,
bool? notificationsEnabled,
bool? breakingNewsNotifications,
bool? trendingNotifications,
bool? liveRemindersNotifications,
bool? sportsNotifications,
}) {
return SettingsState(
expandNavigation: expandNavigation ?? this.expandNavigation,
gpsEnabled: gpsEnabled ?? this.gpsEnabled,
celsiusEnabled: celsiusEnabled ?? this.celsiusEnabled,
notificationsEnabled: notificationsEnabled ?? this.notificationsEnabled,
breakingNewsNotifications:
breakingNewsNotifications ?? this.breakingNewsNotifications,
trendingNotifications:
trendingNotifications ?? this.trendingNotifications,
liveRemindersNotifications:
liveRemindersNotifications ?? this.liveRemindersNotifications,
sportsNotifications: sportsNotifications ?? this.sportsNotifications,
);
}
// mapping user settings
Map<String, dynamic> toMap() {
return {
'expandNavigation': expandNavigation,
'gpsEnabled': gpsEnabled,
'celsiusEnabled': celsiusEnabled,
'notificationsEnabled': notificationsEnabled,
'breakingNewsNotifications': breakingNewsNotifications,
'trendingNotifications': trendingNotifications,
'liveRemindersNotifications': liveRemindersNotifications,
'sportsNotifications': sportsNotifications,
};
}
// grabbing settings state from map
factory SettingsState.fromMap(Map<String, dynamic> map) {
if (map == map['']) {
return SettingsState(
expandNavigation: map[''],
gpsEnabled: map[''],
celsiusEnabled: map[''],
notificationsEnabled: map[''],
breakingNewsNotifications: map[''],
trendingNotifications: map[''],
liveRemindersNotifications: map[''],
sportsNotifications: map[''],
);
} else {
return SettingsState(
expandNavigation: map['expandNavigation'],
gpsEnabled: map['gpsEnabled'],
celsiusEnabled: map['celsiusEnabled'],
notificationsEnabled: map['notificationsEnabled'],
breakingNewsNotifications: map['breakingNewsNotifications'],
trendingNotifications: map['trendingNotifications'],
liveRemindersNotifications: map['liveRemindersNotifications'],
sportsNotifications: map['sportsNotifications'],
);
}
}
// encode to a json
String toJson() => json.encode(toMap());
// decode the json
factory SettingsState.fromJson(String source) =>
SettingsState.fromMap(json.decode(source));
//! Helps for equatable and unit testing
@override
List<Object?> get props => [
expandNavigation,
gpsEnabled,
celsiusEnabled,
notificationsEnabled,
breakingNewsNotifications,
trendingNotifications,
liveRemindersNotifications,
sportsNotifications,
];
//! Shows changes in State for Bloc Observers
@override
String toString() => 'SettingsState(expandNavigation: $expandNavigation, '
'gpsEnabled: $gpsEnabled, celsiusEnabled: $celsiusEnabled, notificationsEnabled: $notificationsEnabled,'
'breakingNewsNotifications: $breakingNewsNotifications, trendingNotifications: $trendingNotifications,'
' liveRemindersNotifications: $liveRemindersNotifications, sportsNotifications: $sportsNotifications)';
}
设置Cubit文件:
import 'dart:convert';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
part 'settings_state.dart';
//bloc/cubit for the settings screen
class SettingsCubit extends Cubit<SettingsState> with HydratedMixin {
SettingsCubit()
: super(SettingsState(
expandNavigation: false,
gpsEnabled: false,
celsiusEnabled: false,
notificationsEnabled: false,
breakingNewsNotifications: false,
trendingNotifications: false,
liveRemindersNotifications: false,
sportsNotifications: false,
));
// Toggle for Expanding Navigation
void toggleExpandNavigation(bool newValue) =>
emit(state.copyWith(expandNavigation: newValue));
// Toggle for GPS
void toggleGpsEnabled(bool newValue) =>
emit(state.copyWith(gpsEnabled: newValue));
// Toggle for Celsius or Fahrenheit
void toggleCelsiusEnabled(bool newValue) =>
emit(state.copyWith(celsiusEnabled: newValue));
// Toggle for All Notifications
void toggleNotificationsEnabled(bool newValue) {
emit(state.copyWith(notificationsEnabled: newValue));
}
// Toggle for Breaking News Notifications
void toggleBreakingNewsNotifications(bool newValue) {
emit(state.copyWith(breakingNewsNotifications: newValue));
}
// Toggle for Trending Notifications
void toggleTrendingNotifications(bool newValue) {
emit(state.copyWith(trendingNotifications: newValue));
}
// Toggle for Live Reminders Notifications
void toggleLiveRemindersNotifications(bool newValue) {
emit(state.copyWith(liveRemindersNotifications: newValue));
}
// Toggle for Sports Notifications
void toggleSportsNotifications(bool newValue) {
emit(state.copyWith(sportsNotifications: newValue));
}
// retrieving from phone storage - json
@override
SettingsState? fromJson(Map<String, dynamic> json) {
return SettingsState.fromMap(json);
}
// writing to phone storage - json
@override
Map<String, dynamic>? toJson(SettingsState state) {
return state.toMap();
}
}
BLoC 测试文件:
import 'package:bloc_test/bloc_test.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/services.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:news_test_app/logic/cubit/settings_cubit.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path_provider/path_provider.dart';
import 'package:mocktail/mocktail.dart';
class MockCubit extends Mock implements SettingsCubit {}
class MockStorage extends Mock implements Storage {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('SettingsCubit', () {
SettingsCubit settingsCubit = SettingsCubit();
Storage storage;
setUp(() {
storage = MockStorage();
HydratedBloc.storage = storage;
settingsCubit = SettingsCubit();
});
tearDown(() {
settingsCubit.close();
});
blocTest<SettingsCubit, SettingsState>(
'the cubit should emit a SettingsState(expandNavigation: true, gpsEnabled: false, celsiusEnabled: false, notificationsEnabled: false, '
'breakingNewsNotifications: false, trendingNotifications: false, liveRemindersNotifications: false, sportsNotifications: false) '
'when cubit.toggleExpandNavigation is called',
build: () => settingsCubit,
act: (cubit) => settingsCubit.toggleExpandNavigation(true),
expect: () => [
settingsCubit.state,
//settingsCubit.state,
// SettingsState(
// expandNavigation: false,
// gpsEnabled: false,
// celsiusEnabled: false,
// notificationsEnabled: false,
// breakingNewsNotifications: false,
// trendingNotifications: false,
// liveRemindersNotifications: false,
// sportsNotifications: false,
// ),
]);
});
}
控制台输出为:
package:test_api expect
package:bloc_test/src/bloc_test.dart 193:9 testBloc.<fn>
===== asynchronous gap ===========================
dart:async _asyncThenWrapperHelper
package:bloc_test/src/bloc_test.dart testBloc.<fn>
dart:async runZonedGuarded
package:bloc_test/src/bloc_test.dart 172:9 testBloc
package:bloc_test/src/bloc_test.dart 140:11 blocTest.<fn>
package:bloc_test/src/bloc_test.dart 139:26 blocTest.<fn>
Expected: [
SettingsState:SettingsState(expandNavigation: false, gpsEnabled: false, celsiusEnabled: false, notificationsEnabled: false,breakingNewsNotifications: false, trendingNotifications: false, liveRemindersNotifications: false, sportsNotifications: false)
]
Actual: []
Which: at location [0] is [] which shorter than expected