fixed unit tests

This commit is contained in:
Vadim 2024-10-29 10:20:55 +01:00
parent 09d04c665f
commit 7db43fb83b
5 changed files with 169 additions and 161 deletions

View file

@ -58,7 +58,6 @@ class ApplicationWrapper extends StatelessWidget {
storageService: iapService,
child: FilmsProvider(
filmsStorageService: filmsStorageService,
storageService: iapService,
child: UserPreferencesProvider(
hasLightSensor: hasLightSensor,
userPreferencesService: userPreferencesService,

View file

@ -5,12 +5,10 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class FilmsProvider extends StatefulWidget {
final FilmsStorageService filmsStorageService;
final IAPStorageService storageService;
final Widget child;
const FilmsProvider({
required this.filmsStorageService,
required this.storageService,
required this.child,
super.key,
});
@ -39,8 +37,8 @@ class FilmsProviderState extends State<FilmsProvider> {
@override
Widget build(BuildContext context) {
return Films(
predefinedFilms: predefinedFilms,
customFilms: customFilms,
predefinedFilms: context.isPro ? predefinedFilms : {},
customFilms: context.isPro ? customFilms : {},
selected: context.isPro ? _selectedFilm : const FilmStub(),
child: widget.child,
);
@ -64,7 +62,7 @@ class FilmsProviderState extends State<FilmsProvider> {
void selectFilm(Film film) {
if (_selectedFilm != film) {
_selectedId = film.id;
widget.storageService.selectedFilmId = _selectedId;
widget.filmsStorageService.selectedFilmId = _selectedId;
setState(() {});
}
}
@ -91,7 +89,7 @@ class FilmsProviderState extends State<FilmsProvider> {
}
Future<void> _init() async {
_selectedId = widget.storageService.selectedFilmId;
_selectedId = widget.filmsStorageService.selectedFilmId;
predefinedFilms.addAll(await widget.filmsStorageService.getPredefinedFilms());
customFilms.addAll(await widget.filmsStorageService.getCustomFilms());
_discardSelectedIfNotIncluded();
@ -105,7 +103,7 @@ class FilmsProviderState extends State<FilmsProvider> {
final isSelectedUsed = predefinedFilms[_selectedId]?.isUsed ?? customFilms[_selectedId]?.isUsed ?? false;
if (!isSelectedUsed) {
_selectedId = const FilmStub().id;
widget.storageService.selectedFilmId = _selectedId;
widget.filmsStorageService.selectedFilmId = _selectedId;
}
}
}

View file

@ -5,18 +5,29 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:mocktail/mocktail.dart';
class _MockIAPStorageService extends Mock implements IAPStorageService {}
class _MockFilmsStorageService extends Mock implements FilmsStorageService {}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late _MockIAPStorageService mockIAPStorageService;
late _MockFilmsStorageService mockFilmsStorageService;
setUpAll(() {
mockIAPStorageService = _MockIAPStorageService();
mockFilmsStorageService = _MockFilmsStorageService();
});
setUp(() {
registerFallbackValue(mockCustomFilms.first);
when(() => mockFilmsStorageService.toggleFilm(any<Film>(), any<bool>())).thenAnswer((_) async {});
when(() => mockFilmsStorageService.addFilm(any<FilmExponential>())).thenAnswer((_) async {});
when(() => mockFilmsStorageService.updateFilm(any<FilmExponential>())).thenAnswer((_) async {});
when(() => mockFilmsStorageService.deleteFilm(any<FilmExponential>())).thenAnswer((_) async {});
when(() => mockFilmsStorageService.getPredefinedFilms())
.thenAnswer((_) => Future.value(mockPredefinedFilms.toFilmsMap()));
when(() => mockFilmsStorageService.getCustomFilms()).thenAnswer((_) => Future.value(mockCustomFilms.toFilmsMap()));
});
tearDown(() {
reset(mockIAPStorageService);
reset(mockFilmsStorageService);
});
Future<void> pumpTestWidget(WidgetTester tester, IAPProductStatus productStatus) async {
@ -30,16 +41,20 @@ void main() {
),
],
child: FilmsProvider(
storageService: mockIAPStorageService,
availableFilms: mockFilms,
filmsStorageService: mockFilmsStorageService,
child: const _Application(),
),
),
);
await tester.pumpAndSettle();
}
void expectFilmsCount(int count) {
expect(find.text('Films count: $count'), findsOneWidget);
void expectPredefinedFilmsCount(int count) {
expect(find.text('Predefined films count: $count'), findsOneWidget);
}
void expectCustomFilmsCount(int count) {
expect(find.text('Custom films count: $count'), findsOneWidget);
}
void expectFilmsInUseCount(int count) {
@ -54,17 +69,21 @@ void main() {
'FilmsProvider dependency on IAPProductStatus',
() {
setUp(() {
when(() => mockIAPStorageService.selectedFilm).thenReturn(mockFilms.first);
when(() => mockIAPStorageService.filmsInUse).thenReturn(mockFilms);
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(mockPredefinedFilms.first.id);
when(() => mockFilmsStorageService.getPredefinedFilms())
.thenAnswer((_) => Future.value(mockPredefinedFilms.toFilmsMap()));
when(() => mockFilmsStorageService.getCustomFilms())
.thenAnswer((_) => Future.value(mockCustomFilms.toFilmsMap()));
});
testWidgets(
'IAPProductStatus.purchased - show all saved films',
(tester) async {
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectFilmsCount(mockFilms.length + 1);
expectFilmsInUseCount(mockFilms.length + 1);
expectSelectedFilmName(mockFilms.first.name);
expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(mockCustomFilms.length);
expectFilmsInUseCount(mockPredefinedFilms.length + mockCustomFilms.length + 1);
expectSelectedFilmName(mockPredefinedFilms.first.name);
},
);
@ -72,7 +91,8 @@ void main() {
'IAPProductStatus.purchasable - show only default',
(tester) async {
await pumpTestWidget(tester, IAPProductStatus.purchasable);
expectFilmsCount(mockFilms.length + 1);
expectPredefinedFilmsCount(0);
expectCustomFilmsCount(0);
expectFilmsInUseCount(1);
expectSelectedFilmName('');
},
@ -82,7 +102,8 @@ void main() {
'IAPProductStatus.pending - show only default',
(tester) async {
await pumpTestWidget(tester, IAPProductStatus.pending);
expectFilmsCount(mockFilms.length + 1);
expectPredefinedFilmsCount(0);
expectCustomFilmsCount(0);
expectFilmsInUseCount(1);
expectSelectedFilmName('');
},
@ -91,126 +112,126 @@ void main() {
);
group(
'FilmsProvider CRUD',
'toggleFilm',
() {
testWidgets(
'Select films in use',
'toggle predefined film',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(const FilmStub());
when(() => mockIAPStorageService.filmsInUse).thenReturn([]);
/// Init
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(mockPredefinedFilms.first.id);
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectFilmsCount(mockFilms.length + 1);
expectFilmsInUseCount(1);
expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(mockCustomFilms.length);
expectFilmsInUseCount(mockPredefinedFilms.length + mockCustomFilms.length + 1);
expectSelectedFilmName(mockPredefinedFilms.first.name);
await tester.filmsProvider.toggleFilm(mockPredefinedFilms.first, false);
await tester.pump();
expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(mockCustomFilms.length);
expectFilmsInUseCount(mockPredefinedFilms.length - 1 + mockCustomFilms.length + 1);
expectSelectedFilmName('');
/// Select all filmsInUse
await tester.tap(find.byKey(_Application.saveFilmsButtonKey(0)));
await tester.pumpAndSettle();
expectFilmsCount(mockFilms.length + 1);
expectFilmsInUseCount(mockFilms.length + 1);
expectSelectedFilmName('');
verify(() => mockIAPStorageService.filmsInUse = mockFilms.skip(0).toList()).called(1);
verifyNever(() => mockIAPStorageService.selectedFilm = const FilmStub());
verify(() => mockFilmsStorageService.toggleFilm(mockPredefinedFilms.first, false)).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = '').called(1);
},
);
testWidgets(
'Select film',
'toggle custom film',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(const FilmStub());
when(() => mockIAPStorageService.filmsInUse).thenReturn(mockFilms);
/// Init
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(mockCustomFilms.first.id);
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectFilmsCount(mockFilms.length + 1);
expectFilmsInUseCount(mockFilms.length + 1);
expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(mockCustomFilms.length);
expectFilmsInUseCount(mockPredefinedFilms.length + mockCustomFilms.length + 1);
expectSelectedFilmName(mockCustomFilms.first.name);
await tester.filmsProvider.toggleFilm(mockCustomFilms.first, false);
await tester.pump();
expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(mockCustomFilms.length);
expectFilmsInUseCount(mockPredefinedFilms.length - 1 + mockCustomFilms.length + 1);
expectSelectedFilmName('');
/// Select all filmsInUse
await tester.tap(find.byKey(_Application.setFilmButtonKey(0)));
await tester.pumpAndSettle();
expectFilmsCount(mockFilms.length + 1);
expectFilmsInUseCount(mockFilms.length + 1);
expectSelectedFilmName(mockFilms.first.name);
verifyNever(() => mockIAPStorageService.filmsInUse = any<List<Film>>());
verify(() => mockIAPStorageService.selectedFilm = mockFilms.first).called(1);
},
);
group(
'Coming from free app',
() {
testWidgets(
'Has selected film',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(mockFilms[2]);
when(() => mockIAPStorageService.filmsInUse).thenReturn([]);
/// Init
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectFilmsInUseCount(1);
expectSelectedFilmName('');
verifyNever(() => mockIAPStorageService.filmsInUse = any<List<Film>>());
verify(() => mockIAPStorageService.selectedFilm = const FilmStub()).called(1);
},
);
testWidgets(
'None film selected',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(const FilmStub());
when(() => mockIAPStorageService.filmsInUse).thenReturn([]);
/// Init
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectFilmsInUseCount(1);
expectSelectedFilmName('');
verifyNever(() => mockIAPStorageService.filmsInUse = any<List<Film>>());
verifyNever(() => mockIAPStorageService.selectedFilm = const FilmStub());
},
);
},
);
testWidgets(
'Discard selected (by filmsInUse list update)',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(mockFilms.first);
when(() => mockIAPStorageService.filmsInUse).thenReturn(mockFilms);
/// Init
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectFilmsCount(mockFilms.length + 1);
expectFilmsInUseCount(mockFilms.length + 1);
expectSelectedFilmName(mockFilms.first.name);
/// Select all filmsInUse except the first one
await tester.tap(find.byKey(_Application.saveFilmsButtonKey(1)));
await tester.pumpAndSettle();
expectFilmsCount(mockFilms.length + 1);
expectFilmsInUseCount((mockFilms.length - 1) + 1);
expectSelectedFilmName('');
verify(() => mockIAPStorageService.filmsInUse = mockFilms.skip(1).toList()).called(1);
verify(() => mockIAPStorageService.selectedFilm = const FilmStub()).called(1);
verify(() => mockFilmsStorageService.toggleFilm(mockCustomFilms.first, false)).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = '').called(1);
},
);
},
);
testWidgets(
'selectFilm',
(tester) async {
when(() => mockFilmsStorageService.selectedFilmId).thenReturn('');
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectSelectedFilmName('');
tester.filmsProvider.selectFilm(mockPredefinedFilms.first);
await tester.pump();
expectSelectedFilmName(mockPredefinedFilms.first.name);
tester.filmsProvider.selectFilm(mockCustomFilms.first);
await tester.pump();
expectSelectedFilmName(mockCustomFilms.first.name);
verify(() => mockFilmsStorageService.selectedFilmId = mockPredefinedFilms.first.id).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = mockCustomFilms.first.id).called(1);
},
);
testWidgets(
'Custom film CRUD',
(tester) async {
when(() => mockFilmsStorageService.selectedFilmId).thenReturn('');
when(() => mockFilmsStorageService.getCustomFilms()).thenAnswer((_) => Future.value({}));
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(0);
expectFilmsInUseCount(mockPredefinedFilms.length + 0 + 1);
expectSelectedFilmName('');
await tester.filmsProvider.addCustomFilm(mockCustomFilms.first);
await tester.filmsProvider.toggleFilm(mockCustomFilms.first, true);
tester.filmsProvider.selectFilm(mockCustomFilms.first);
await tester.pump();
expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(1);
expectFilmsInUseCount(mockPredefinedFilms.length + 1 + 1);
expectSelectedFilmName(mockCustomFilms.first.name);
verify(() => mockFilmsStorageService.addFilm(mockCustomFilms.first)).called(1);
verify(() => mockFilmsStorageService.toggleFilm(mockCustomFilms.first, true)).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = mockCustomFilms.first.id).called(1);
const editedFilmName = 'Edited custom film 2x';
final editedFilm = mockCustomFilms.first.copyWith(name: editedFilmName);
await tester.filmsProvider.updateCustomFilm(editedFilm);
await tester.pump();
expectSelectedFilmName(editedFilm.name);
verify(() => mockFilmsStorageService.updateFilm(editedFilm)).called(1);
await tester.filmsProvider.deleteCustomFilm(editedFilm);
await tester.pump();
expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(0);
expectFilmsInUseCount(mockPredefinedFilms.length + 0 + 1);
expectSelectedFilmName('');
verify(() => mockFilmsStorageService.deleteFilm(editedFilm)).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = '').called(1);
},
);
}
extension on WidgetTester {
FilmsProviderState get filmsProvider {
final BuildContext context = element(find.byType(_Application));
return FilmsProvider.of(context);
}
}
class _Application extends StatelessWidget {
const _Application();
static ValueKey saveFilmsButtonKey(int index) => ValueKey('saveFilmsButtonKey$index');
static ValueKey setFilmButtonKey(int index) => ValueKey('setFilmButtonKey$index');
@override
Widget build(BuildContext context) {
return MaterialApp(
@ -218,42 +239,30 @@ class _Application extends StatelessWidget {
body: Center(
child: Column(
children: [
Text("Films count: ${Films.of(context).length}"),
Text("Predefined films count: ${Films.predefinedFilmsOf(context).length}"),
Text("Custom films count: ${Films.customFilmsOf(context).length}"),
Text("Films in use count: ${Films.inUseOf(context).length}"),
Text("Selected film: ${Films.selectedOf(context).name}"),
_filmRow(context, 0),
_filmRow(context, 1),
],
),
),
),
);
}
Widget _filmRow(BuildContext context, int index) {
return Row(
children: [
ElevatedButton(
key: saveFilmsButtonKey(index),
onPressed: () {
FilmsProvider.of(context).saveFilms(mockFilms.skip(index).toList());
},
child: const Text("Save filmsInUse"),
),
ElevatedButton(
key: setFilmButtonKey(index),
onPressed: () {
FilmsProvider.of(context).setFilm(mockFilms[index]);
},
child: const Text("Set film"),
),
],
);
}
}
const mockFilms = [
const mockPredefinedFilms = [
FilmExponential(id: '1', name: 'Mock film 2x', iso: 400, exponent: 1.34),
FilmExponential(id: '2', name: 'Mock film 3x', iso: 800, exponent: 1.34),
FilmExponential(id: '3', name: 'Mock film 4x', iso: 1200, exponent: 1.34),
];
const mockCustomFilms = [
FilmExponential(id: '1abc', name: 'Mock custom film 2x', iso: 400, exponent: 1.34),
FilmExponential(id: '2abc', name: 'Mock custom film 3x', iso: 800, exponent: 1.34),
];
extension on List<Film> {
Map<String, ({T film, bool isUsed})> toFilmsMap<T extends Film>() =>
Map.fromEntries(map((e) => MapEntry(e.id, (film: e as T, isUsed: true))));
}

View file

@ -50,8 +50,8 @@ extension WidgetTesterActions on WidgetTester {
}) async {
await pumpWidget(
Films(
values: const [FilmStub()],
filmsInUse: const [FilmStub()],
predefinedFilms: const {},
customFilms: const {},
selected: const FilmStub(),
child: WidgetTestApplicationMock(
child: Row(

View file

@ -10,14 +10,16 @@ import 'package:mocktail/mocktail.dart';
import '../../../../../application_mock.dart';
import 'utils.dart';
class _MockIAPStorageService extends Mock implements IAPStorageService {}
class _MockFilmsStorageService extends Mock implements FilmsStorageService {}
void main() {
late final _MockIAPStorageService mockIAPStorageService;
late final _MockFilmsStorageService mockFilmsStorageService;
setUpAll(() {
mockIAPStorageService = _MockIAPStorageService();
when(() => mockIAPStorageService.filmsInUse).thenReturn(_films);
mockFilmsStorageService = _MockFilmsStorageService();
when(() => mockFilmsStorageService.getPredefinedFilms()).thenAnswer(
(_) => Future.value(Map.fromEntries(_films.map((e) => MapEntry(e.id, (film: e, isUsed: true))))),
);
});
Future<void> pumpApplication(WidgetTester tester) async {
@ -31,7 +33,7 @@ void main() {
),
],
child: FilmsProvider(
storageService: mockIAPStorageService,
filmsStorageService: mockFilmsStorageService,
child: const WidgetTestApplicationMock(
child: Row(
children: [
@ -51,7 +53,7 @@ void main() {
testWidgets(
'Film.other()',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(const FilmStub());
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(const FilmStub().id);
await pumpApplication(tester);
expectReadingValueContainerText(S.current.film);
expectReadingValueContainerText(S.current.none);
@ -61,7 +63,7 @@ void main() {
testWidgets(
'Film with the same ISO',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(_films[1]);
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(_films[1].id);
await pumpApplication(tester);
expectReadingValueContainerText(S.current.film);
expectReadingValueContainerText(_films[1].name);
@ -71,7 +73,7 @@ void main() {
testWidgets(
'Film with greater ISO',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(_films[2]);
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(_films[2].id);
await pumpApplication(tester);
expectReadingValueContainerText(S.current.filmPull);
expectReadingValueContainerText(_films[2].name);
@ -81,7 +83,7 @@ void main() {
testWidgets(
'Film with lower ISO',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(_films[0]);
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(_films[0].id);
await pumpApplication(tester);
expectReadingValueContainerText(S.current.filmPush);
expectReadingValueContainerText(_films[0].name);
@ -92,7 +94,7 @@ void main() {
testWidgets(
'Film picker shows only films in use',
(tester) async {
when(() => mockIAPStorageService.selectedFilm).thenReturn(_films[0]);
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(_films[0].id);
await pumpApplication(tester);
await tester.openAnimatedPicker<FilmPicker>();
expectRadioListTile<Film>(S.current.none, isSelected: true);
@ -104,8 +106,8 @@ void main() {
}
const _films = [
FilmExponential(id: '', name: 'ISO 100 Film', iso: 100, exponent: 1.34),
FilmExponential(id: '', name: 'ISO 400 Film', iso: 400, exponent: 1.34),
FilmExponential(id: '', name: 'ISO 800 Film', iso: 800, exponent: 1.34),
FilmExponential(id: '', name: 'ISO 1600 Film', iso: 1600, exponent: 1.34),
FilmExponential(id: '1', name: 'ISO 100 Film', iso: 100, exponent: 1.34),
FilmExponential(id: '2', name: 'ISO 400 Film', iso: 400, exponent: 1.34),
FilmExponential(id: '3', name: 'ISO 800 Film', iso: 800, exponent: 1.34),
FilmExponential(id: '4', name: 'ISO 1600 Film', iso: 1600, exponent: 1.34),
];