Skip to content

Commit

Permalink
feat(hydrated_bloc)!: HydratedBlocOverrides API
Browse files Browse the repository at this point in the history
  • Loading branch information
felangel committed Nov 15, 2021
1 parent f155709 commit 41aadc8
Show file tree
Hide file tree
Showing 14 changed files with 1,141 additions and 687 deletions.
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
20 changes: 11 additions & 9 deletions examples/flutter_weather/test/helpers/hydrated_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:mocktail/mocktail.dart';

class MockStorage extends Mock implements Storage {}

late Storage hydratedStorage;
class MockStorage extends Mock implements Storage {
MockStorage() {
TestWidgetsFlutterBinding.ensureInitialized();
when(() => write(any(), any<dynamic>())).thenAnswer((_) async {});
}
}

void initHydratedBloc() {
TestWidgetsFlutterBinding.ensureInitialized();
hydratedStorage = MockStorage();
when(() => hydratedStorage.write(any(), any<dynamic>()))
.thenAnswer((_) async {});
HydratedBloc.storage = hydratedStorage;
T mockHydratedStorage<T>(T Function() body, {Storage? storage}) {
return HydratedBlocOverrides.runZoned<T>(
body,
storage: storage ?? MockStorage(),
);
}
28 changes: 16 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 @@ -2,6 +2,7 @@ import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter_weather/theme/cubit/theme_cubit.dart';
import 'package:flutter_weather/weather/weather.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';

Expand All @@ -18,19 +19,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 +47,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

0 comments on commit 41aadc8

Please sign in to comment.