From 51ab7aca7b59854ba6be5dec65a38edceb1225be Mon Sep 17 00:00:00 2001 From: Gabriel Costa de Oliveira Date: Mon, 20 Jan 2025 12:59:33 -0300 Subject: [PATCH] test(#58): corrigindo tela e criando casos de teste --- .../edit_profile/view/edit_profile_view.dart | 35 ++- .../viewmodel/edit_profile_viewmodel.dart | 27 +- .../view/edit_profile_view_test.dart | 97 +++++++ .../view/edit_profile_view_test.mocks.dart | 253 ++++++++++++++++++ 4 files changed, 379 insertions(+), 33 deletions(-) create mode 100644 test/ui/edit_profile/view/edit_profile_view_test.dart create mode 100644 test/ui/edit_profile/view/edit_profile_view_test.mocks.dart diff --git a/lib/ui/edit_profile/view/edit_profile_view.dart b/lib/ui/edit_profile/view/edit_profile_view.dart index d58d3fe..7c6d240 100644 --- a/lib/ui/edit_profile/view/edit_profile_view.dart +++ b/lib/ui/edit_profile/view/edit_profile_view.dart @@ -1,5 +1,5 @@ +import 'package:aranduapp/ui/edit_profile/model/edit_profile_request.dart'; import 'package:aranduapp/ui/edit_profile/viewmodel/edit_profile_viewmodel.dart'; -import 'package:aranduapp/ui/login/viewmodel/login_viewmodel.dart'; import 'package:aranduapp/ui/shared/text_email.dart'; import 'package:aranduapp/ui/shared/text_name.dart'; import 'package:aranduapp/ui/shared/command_button.dart'; @@ -12,15 +12,20 @@ class EditProfile extends StatelessWidget { @override Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: GetIt.instance(), - child: const EditProfileScreen(), + return ChangeNotifierProvider.value( + value: GetIt.instance(), + child: EditProfileScreen(), ); } } class EditProfileScreen extends StatelessWidget { - const EditProfileScreen({super.key}); + final GlobalKey formKey = GlobalKey(); + final TextEditingController nameController = TextEditingController(); + final TextEditingController userNameController = TextEditingController(); + final TextEditingController emailController = TextEditingController(); + + EditProfileScreen({super.key}); @override Widget build(BuildContext context) { @@ -60,24 +65,27 @@ class EditProfileScreen extends StatelessWidget { Widget _buildForm(BuildContext context, EditProfileViewModel viewModel) { return Form( - key: viewModel.formKey, + key: formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextName( - controller: viewModel.nameController, + key: const Key("nameController"), + controller: nameController, padding: const EdgeInsets.symmetric(vertical: 0), ), const SizedBox(height: 20), TextName( + key: const Key("userNameController"), label: "Nome de Usuário", - controller: viewModel.userNameController, + controller: userNameController, padding: const EdgeInsets.symmetric(vertical: 0), ), const SizedBox(height: 20), TextEmail( + key: const Key("emailNameController"), padding: const EdgeInsets.symmetric(vertical: 0), - controller: viewModel.emailController, + controller: emailController, ), const SizedBox(height: 100), _saveButton(context, viewModel), @@ -90,7 +98,14 @@ class EditProfileScreen extends StatelessWidget { Widget _saveButton(BuildContext context, EditProfileViewModel viewModel) { return CommandButton( - tap: viewModel.editCommand.execute , + tap: () { + if (formKey.currentState!.validate()) { + viewModel.editCommand.execute(EditProfileRequest( + name: nameController.text, + email: emailController.text, + userName: userNameController.text)); + } + }, command: viewModel.editCommand, nameButton: "Salvar", onErrorCallback: (e) { diff --git a/lib/ui/edit_profile/viewmodel/edit_profile_viewmodel.dart b/lib/ui/edit_profile/viewmodel/edit_profile_viewmodel.dart index 8e588a3..6045570 100644 --- a/lib/ui/edit_profile/viewmodel/edit_profile_viewmodel.dart +++ b/lib/ui/edit_profile/viewmodel/edit_profile_viewmodel.dart @@ -6,33 +6,14 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; class EditProfileViewModel extends ChangeNotifier { - final GlobalKey formKey; - final TextEditingController nameController; - final TextEditingController userNameController; - final TextEditingController emailController; + late Command1 editCommand; - late Command0 editCommand; - - EditProfileViewModel() - : formKey = GlobalKey(), - nameController = TextEditingController(), - userNameController = TextEditingController(), - emailController = TextEditingController() { - editCommand = Command0(editProfile); + EditProfileViewModel() { + editCommand = Command1(editProfile); } - Future> editProfile() async { - if (!formKey.currentState!.validate()) { - return Result.error(Exception('Valores inválidos')); - } - - EditProfileRequest request = EditProfileRequest( - name: nameController.text, - userName: userNameController.text, - email: emailController.text); - + Future> editProfile(EditProfileRequest request) async { await GetIt.instance().edit(request); - return Result.value(null); } } diff --git a/test/ui/edit_profile/view/edit_profile_view_test.dart b/test/ui/edit_profile/view/edit_profile_view_test.dart new file mode 100644 index 0000000..d64d7c9 --- /dev/null +++ b/test/ui/edit_profile/view/edit_profile_view_test.dart @@ -0,0 +1,97 @@ +import 'package:aranduapp/core/state/command.dart'; +import 'package:aranduapp/ui/edit_profile/model/edit_profile_request.dart'; +import 'package:aranduapp/ui/edit_profile/view/edit_profile_view.dart'; +import 'package:aranduapp/ui/edit_profile/viewmodel/edit_profile_viewmodel.dart'; +import 'package:aranduapp/ui/shared/command_button.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'edit_profile_view_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec(), MockSpec()]) + +void main() { + + late MockEditProfileViewModel mockEditProfileViewModel; + late MockCommand1 mockEditProfileCommand1; + + setUp(() async { + mockEditProfileViewModel =MockEditProfileViewModel(); + + mockEditProfileCommand1 = MockCommand1(); + + when(mockEditProfileViewModel.editCommand) + .thenReturn(mockEditProfileCommand1); + + when(mockEditProfileCommand1.running).thenReturn(false); + when(mockEditProfileCommand1.isError).thenReturn(false); + when(mockEditProfileCommand1.isOk).thenReturn(false); + + await GetIt.instance.reset(); + GetIt.I.registerLazySingleton( + () => mockEditProfileViewModel); + }); + + Widget createScreen() { + return const MaterialApp( + home: EditProfile(), + ); + } + + testWidgets('edit profile screen displays', (WidgetTester tester) async { + await tester.pumpWidget(createScreen()); + await tester.pumpAndSettle(); + + expect(find.byType(AppBar), findsOneWidget); + expect(find.byKey(const ValueKey('nameController')), findsOneWidget); + expect(find.byKey(const ValueKey('userNameController')), findsOneWidget); + expect(find.byKey(const ValueKey('emailNameController')), findsOneWidget); + expect(find.byType(CommandButton), findsOneWidget); + }); + + + + testWidgets('edit password right values', (WidgetTester tester) async { + await tester.pumpWidget(createScreen()); + + const name = 'gabriel'; + const email = 'gab@gmail.com'; + const userName = 'dark'; + + await tester.enterText(find.byKey(const ValueKey('nameController')), name); + await tester.enterText(find.byKey(const ValueKey('emailNameController')), email); + await tester.enterText(find.byKey(const ValueKey('userNameController')), userName); + + await tester.tap(find.byKey(const Key('elevated_button_key'))); + await tester.pumpAndSettle(); + + verify(mockEditProfileCommand1.execute(argThat( + predicate((req) => + req.name == name && + req.email == email && + req.userName == userName + )))).called(1); + }); + + testWidgets('edit password wrong values', (WidgetTester tester) async { + await tester.pumpWidget(createScreen()); + + const name = 'gabriel'; + const email = 'gab'; + const userName = 'dark'; + + await tester.enterText(find.byKey(const ValueKey('nameController')), name); + await tester.enterText(find.byKey(const ValueKey('emailNameController')), email); + await tester.enterText(find.byKey(const ValueKey('userNameController')), userName); + + await tester.tap(find.byKey(const Key('elevated_button_key'))); + await tester.pumpAndSettle(); + + verifyNever(mockEditProfileCommand1.execute(argThat(isNotNull))); + }); + +} + diff --git a/test/ui/edit_profile/view/edit_profile_view_test.mocks.dart b/test/ui/edit_profile/view/edit_profile_view_test.mocks.dart new file mode 100644 index 0000000..71a78aa --- /dev/null +++ b/test/ui/edit_profile/view/edit_profile_view_test.mocks.dart @@ -0,0 +1,253 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in aranduapp/test/ui/edit_profile/view/edit_profile_view_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i6; + +import 'package:aranduapp/core/state/command.dart' as _i2; +import 'package:aranduapp/ui/edit_profile/model/edit_profile_request.dart' + as _i5; +import 'package:aranduapp/ui/edit_profile/viewmodel/edit_profile_viewmodel.dart' + as _i4; +import 'package:async/async.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeCommand1_0 extends _i1.SmartFake + implements _i2.Command1 { + _FakeCommand1_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeResult_1 extends _i1.SmartFake implements _i3.Result { + _FakeResult_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [EditProfileViewModel]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockEditProfileViewModel extends _i1.Mock + implements _i4.EditProfileViewModel { + @override + _i2.Command1 get editCommand => + (super.noSuchMethod( + Invocation.getter(#editCommand), + returnValue: _FakeCommand1_0( + this, + Invocation.getter(#editCommand), + ), + returnValueForMissingStub: + _FakeCommand1_0( + this, + Invocation.getter(#editCommand), + ), + ) as _i2.Command1); + + @override + set editCommand(_i2.Command1? _editCommand) => + super.noSuchMethod( + Invocation.setter( + #editCommand, + _editCommand, + ), + returnValueForMissingStub: null, + ); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i6.Future<_i3.Result> editProfile(_i5.EditProfileRequest? request) => + (super.noSuchMethod( + Invocation.method( + #editProfile, + [request], + ), + returnValue: _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.method( + #editProfile, + [request], + ), + )), + returnValueForMissingStub: + _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.method( + #editProfile, + [request], + ), + )), + ) as _i6.Future<_i3.Result>); + + @override + void addListener(dynamic listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(dynamic listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [Command1]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCommand1 extends _i1.Mock implements _i2.Command1 { + @override + _i6.Future<_i3.Result> Function(A) get action => (super.noSuchMethod( + Invocation.getter(#action), + returnValue: (A __p0) => + _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.getter(#action), + )), + returnValueForMissingStub: (A __p0) => + _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.getter(#action), + )), + ) as _i6.Future<_i3.Result> Function(A)); + + @override + bool get isError => (super.noSuchMethod( + Invocation.getter(#isError), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get isOk => (super.noSuchMethod( + Invocation.getter(#isOk), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get running => (super.noSuchMethod( + Invocation.getter(#running), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i6.Future<_i3.Result> execute(A? arg1) => (super.noSuchMethod( + Invocation.method( + #execute, + [arg1], + ), + returnValue: _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.method( + #execute, + [arg1], + ), + )), + returnValueForMissingStub: + _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.method( + #execute, + [arg1], + ), + )), + ) as _i6.Future<_i3.Result>); + + @override + void addListener(dynamic listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(dynamic listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +}