Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hydrated_bloc)!: HydratedBlocOverrides API #2947

Merged
merged 2 commits into from
Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/hydrated_bloc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
run: flutter analyze lib test example

- name: Run tests
run: flutter test -j 1 --no-pub --coverage --test-randomize-ordering-seed random
run: flutter test --no-pub --coverage --test-randomize-ordering-seed random

- name: Check Code Coverage
uses: VeryGoodOpenSource/very_good_coverage@v1.2.0
Expand Down
5 changes: 3 additions & 2 deletions examples/flutter_weather/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import 'package:weather_repository/weather_repository.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
HydratedBloc.storage = await HydratedStorage.build(
final storage = await HydratedStorage.build(
storageDirectory: kIsWeb
? HydratedStorage.webStorageDirectory
: await getTemporaryDirectory(),
);
BlocOverrides.runZoned(
HydratedBlocOverrides.runZoned(
() => runApp(WeatherApp(weatherRepository: WeatherRepository())),
blocObserver: WeatherBlocObserver(),
storage: storage,
);
}
42 changes: 27 additions & 15 deletions examples/flutter_weather/test/app_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ class MockThemeCubit extends MockCubit<Color> implements ThemeCubit {}
class MockWeatherRepository extends Mock implements WeatherRepository {}

void main() {
setUpAll(initHydratedBloc);

group('WeatherApp', () {
late WeatherRepository weatherRepository;

Expand All @@ -25,7 +23,11 @@ void main() {
});

testWidgets('renders WeatherAppView', (tester) async {
await tester.pumpWidget(WeatherApp(weatherRepository: weatherRepository));
await mockHydratedStorage(() async {
await tester.pumpWidget(
WeatherApp(weatherRepository: weatherRepository),
);
});
expect(find.byType(WeatherAppView), findsOneWidget);
});
});
Expand All @@ -41,24 +43,34 @@ void main() {

testWidgets('renders WeatherPage', (tester) async {
when(() => themeCubit.state).thenReturn(Colors.blue);
await tester.pumpWidget(
RepositoryProvider.value(
value: weatherRepository,
child: BlocProvider.value(value: themeCubit, child: WeatherAppView()),
),
);
await mockHydratedStorage(() async {
await tester.pumpWidget(
RepositoryProvider.value(
value: weatherRepository,
child: BlocProvider.value(
value: themeCubit,
child: WeatherAppView(),
),
),
);
});
expect(find.byType(WeatherPage), findsOneWidget);
});

testWidgets('has correct theme primary color', (tester) async {
const color = Color(0xFFD2D2D2);
when(() => themeCubit.state).thenReturn(color);
await tester.pumpWidget(
RepositoryProvider.value(
value: weatherRepository,
child: BlocProvider.value(value: themeCubit, child: WeatherAppView()),
),
);
await mockHydratedStorage(() async {
await tester.pumpWidget(
RepositoryProvider.value(
value: weatherRepository,
child: BlocProvider.value(
value: themeCubit,
child: WeatherAppView(),
),
),
);
});
final materialApp = tester.widget<MaterialApp>(find.byType(MaterialApp));
expect(materialApp.theme?.primaryColor, color);
});
Expand Down
16 changes: 10 additions & 6 deletions examples/flutter_weather/test/helpers/hydrated_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import 'package:mocktail/mocktail.dart';

class MockStorage extends Mock implements Storage {}

late Storage hydratedStorage;
T mockHydratedStorage<T>(T Function() body, {Storage? storage}) {
return HydratedBlocOverrides.runZoned<T>(
body,
storage: storage ?? _buildMockStorage(),
);
}

void initHydratedBloc() {
Storage _buildMockStorage() {
TestWidgetsFlutterBinding.ensureInitialized();
hydratedStorage = MockStorage();
when(() => hydratedStorage.write(any(), any<dynamic>()))
.thenAnswer((_) async {});
HydratedBloc.storage = hydratedStorage;
final storage = MockStorage();
when(() => storage.write(any(), any<dynamic>())).thenAnswer((_) async {});
return storage;
}
27 changes: 15 additions & 12 deletions examples/flutter_weather/test/theme/cubit/theme_cubit_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,22 @@ class MockWeather extends Mock implements Weather {
}

void main() {
initHydratedBloc();
group('ThemeCubit', () {
test('initial state is correct', () {
expect(ThemeCubit().state, ThemeCubit.defaultColor);
mockHydratedStorage(() {
expect(ThemeCubit().state, ThemeCubit.defaultColor);
});
});

group('toJson/fromJson', () {
test('work properly', () {
final themeCubit = ThemeCubit();
expect(
themeCubit.fromJson(themeCubit.toJson(themeCubit.state)),
themeCubit.state,
);
mockHydratedStorage(() {
final themeCubit = ThemeCubit();
expect(
themeCubit.fromJson(themeCubit.toJson(themeCubit.state)),
themeCubit.state,
);
});
});
});

Expand All @@ -43,35 +46,35 @@ void main() {

blocTest<ThemeCubit, Color>(
'emits correct color for WeatherCondition.clear',
build: () => ThemeCubit(),
build: () => mockHydratedStorage(() => ThemeCubit()),
act: (cubit) => cubit.updateTheme(clearWeather),
expect: () => <Color>[Colors.orangeAccent],
);

blocTest<ThemeCubit, Color>(
'emits correct color for WeatherCondition.snowy',
build: () => ThemeCubit(),
build: () => mockHydratedStorage(() => ThemeCubit()),
act: (cubit) => cubit.updateTheme(snowyWeather),
expect: () => <Color>[Colors.lightBlueAccent],
);

blocTest<ThemeCubit, Color>(
'emits correct color for WeatherCondition.cloudy',
build: () => ThemeCubit(),
build: () => mockHydratedStorage(() => ThemeCubit()),
act: (cubit) => cubit.updateTheme(cloudyWeather),
expect: () => <Color>[Colors.blueGrey],
);

blocTest<ThemeCubit, Color>(
'emits correct color for WeatherCondition.rainy',
build: () => ThemeCubit(),
build: () => mockHydratedStorage(() => ThemeCubit()),
act: (cubit) => cubit.updateTheme(rainyWeather),
expect: () => <Color>[Colors.indigoAccent],
);

blocTest<ThemeCubit, Color>(
'emits correct color for WeatherCondition.unknown',
build: () => ThemeCubit(),
build: () => mockHydratedStorage(() => ThemeCubit()),
act: (cubit) => cubit.updateTheme(unknownWeather),
expect: () => <Color>[ThemeCubit.defaultColor],
);
Expand Down
50 changes: 26 additions & 24 deletions examples/flutter_weather/test/weather/cubit/weather_cubit_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ void main() {
late weather_repository.Weather weather;
late weather_repository.WeatherRepository weatherRepository;

setUpAll(initHydratedBloc);

setUp(() {
weather = MockWeather();
weatherRepository = MockWeatherRepository();
Expand All @@ -36,38 +34,42 @@ void main() {
});

test('initial state is correct', () {
final weatherCubit = WeatherCubit(weatherRepository);
expect(weatherCubit.state, WeatherState());
mockHydratedStorage(() {
final weatherCubit = WeatherCubit(weatherRepository);
expect(weatherCubit.state, WeatherState());
});
});

group('toJson/fromJson', () {
test('work properly', () {
final weatherCubit = WeatherCubit(weatherRepository);
expect(
weatherCubit.fromJson(weatherCubit.toJson(weatherCubit.state)),
weatherCubit.state,
);
mockHydratedStorage(() {
final weatherCubit = WeatherCubit(weatherRepository);
expect(
weatherCubit.fromJson(weatherCubit.toJson(weatherCubit.state)),
weatherCubit.state,
);
});
});
});

group('fetchWeather', () {
blocTest<WeatherCubit, WeatherState>(
'emits nothing when city is null',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
act: (cubit) => cubit.fetchWeather(null),
expect: () => <WeatherState>[],
);

blocTest<WeatherCubit, WeatherState>(
'emits nothing when city is empty',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
act: (cubit) => cubit.fetchWeather(''),
expect: () => <WeatherState>[],
);

blocTest<WeatherCubit, WeatherState>(
'calls getWeather with correct city',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
act: (cubit) => cubit.fetchWeather(weatherLocation),
verify: (_) {
verify(() => weatherRepository.getWeather(weatherLocation)).called(1);
Expand All @@ -81,7 +83,7 @@ void main() {
() => weatherRepository.getWeather(any()),
).thenThrow(Exception('oops'));
},
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
act: (cubit) => cubit.fetchWeather(weatherLocation),
expect: () => <WeatherState>[
WeatherState(status: WeatherStatus.loading),
Expand All @@ -91,7 +93,7 @@ void main() {

blocTest<WeatherCubit, WeatherState>(
'emits [loading, success] when getWeather returns (celsius)',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
act: (cubit) => cubit.fetchWeather(weatherLocation),
expect: () => <dynamic>[
WeatherState(status: WeatherStatus.loading),
Expand All @@ -115,7 +117,7 @@ void main() {

blocTest<WeatherCubit, WeatherState>(
'emits [loading, success] when getWeather returns (fahrenheit)',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
seed: () => WeatherState(temperatureUnits: TemperatureUnits.fahrenheit),
act: (cubit) => cubit.fetchWeather(weatherLocation),
expect: () => <dynamic>[
Expand Down Expand Up @@ -145,7 +147,7 @@ void main() {
group('refreshWeather', () {
blocTest<WeatherCubit, WeatherState>(
'emits nothing when status is not success',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
act: (cubit) => cubit.refreshWeather(),
expect: () => <WeatherState>[],
verify: (_) {
Expand All @@ -155,7 +157,7 @@ void main() {

blocTest<WeatherCubit, WeatherState>(
'emits nothing when location is null',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
seed: () => WeatherState(status: WeatherStatus.success),
act: (cubit) => cubit.refreshWeather(),
expect: () => <WeatherState>[],
Expand All @@ -166,7 +168,7 @@ void main() {

blocTest<WeatherCubit, WeatherState>(
'invokes getWeather with correct location',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
seed: () => WeatherState(
status: WeatherStatus.success,
weather: Weather(
Expand All @@ -189,7 +191,7 @@ void main() {
() => weatherRepository.getWeather(any()),
).thenThrow(Exception('oops'));
},
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
seed: () => WeatherState(
status: WeatherStatus.success,
weather: Weather(
Expand All @@ -205,7 +207,7 @@ void main() {

blocTest<WeatherCubit, WeatherState>(
'emits updated weather (celsius)',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
seed: () => WeatherState(
status: WeatherStatus.success,
weather: Weather(
Expand Down Expand Up @@ -237,7 +239,7 @@ void main() {

blocTest<WeatherCubit, WeatherState>(
'emits updated weather (fahrenheit)',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
seed: () => WeatherState(
temperatureUnits: TemperatureUnits.fahrenheit,
status: WeatherStatus.success,
Expand Down Expand Up @@ -272,7 +274,7 @@ void main() {
group('toggleUnits', () {
blocTest<WeatherCubit, WeatherState>(
'emits updated units when status is not success',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
act: (cubit) => cubit.toggleUnits(),
expect: () => <WeatherState>[
WeatherState(temperatureUnits: TemperatureUnits.fahrenheit),
Expand All @@ -282,7 +284,7 @@ void main() {
blocTest<WeatherCubit, WeatherState>(
'emits updated units and temperature '
'when status is success (celsius)',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
seed: () => WeatherState(
status: WeatherStatus.success,
temperatureUnits: TemperatureUnits.fahrenheit,
Expand Down Expand Up @@ -311,7 +313,7 @@ void main() {
blocTest<WeatherCubit, WeatherState>(
'emits updated units and temperature '
'when status is success (fahrenheit)',
build: () => WeatherCubit(weatherRepository),
build: () => mockHydratedStorage(() => WeatherCubit(weatherRepository)),
seed: () => WeatherState(
status: WeatherStatus.success,
temperatureUnits: TemperatureUnits.celsius,
Expand Down
Loading