I'm using mockito for testing, riverpod for state management. I'm trying to test the method in my controller class but getting the FakeUsedError:
FakeUsedError: 'execute' No stub was found which matches the argument
of this method call: execute(Instance of 'AuthUseCaseInput').
I'm calling AuthUseCase class method from the AuthController class.
class AuthController extends StateNotifier<AuthState> {
final AuthUseCase authUseCase;
AuthController(this.authUseCase) : super(const AuthState.initial());
Future<void> mapAuthEventToAuthState(AuthEvent event) async {
state = const AuthState.loading();
await event.map(
signInWithEmailAndPassword: (signInWithEmailAndPassword) async {
final result = await authUseCase.execute(AuthUseCaseInput(
signInWithEmailAndPassword.email,
signInWithEmailAndPassword.password));
await result.fold(
(failure) async => state = AuthState.error(failure),
(login) async => state = const AuthState.loggedIn(),
);
});
}
The test class code is given below
void main() {
late AuthUseCase mockAuthUseCase;
late Login login;
late AuthUseCaseInput authUseCaseInput;
late AuthController authController;
setUpAll(() {
mockAuthUseCase = MockAuthUseCase();
login = LoginModel.fromJson(
json.decode(
jsonReader('helpers/dummy_data/login_success_response.json'),
),
).toEntity();
authUseCaseInput = AuthUseCaseInput(email, password);
when(mockAuthUseCase.execute(authUseCaseInput)).thenAnswer(
(_) async => Right(login),
);
authController = AuthController(mockAuthUseCase);
});
group('Auth Controller', () {
stateNotifierTest<AuthController, AuthState>(
'[AuthState.loggedIn] when sign in is success',
setUp: () async {
when(mockAuthUseCase.execute(authUseCaseInput))
.thenAnswer(
(_) async => Right(login),
);
},
actions: (notifier) => notifier.mapAuthEventToAuthState(
const SignInWithEmailAndPassword(email, password)),
expect: () => [const AuthState.loading(), const AuthState.loggedIn()],
build: () {
return authController;
});
});
}
Related
I'm using flutter_app_badger and Firebase Messaging to set a notification system which would update number in app badger when a new notification is coming. I'm working in Flutterflow so my approach might be a bit awkward. So far I managed to do two things:
Create a separate function in the app which updates app badger while app is working;
Set the notification system that is triggered by the action in app;
But I can't combine the two. I have the following push_notification_handler code and trying how to put FlutterAppBadger.updateBadgeCount() function it.
import 'dart:async';
import 'dart:convert';
import 'serialization_util.dart';
import '../backend.dart';
import '../../flutter_flow/flutter_flow_theme.dart';
import '../../flutter_flow/flutter_flow_util.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import '../../index.dart';
import '../../main.dart';
final _handledMessageIds = <String?>{};
class PushNotificationsHandler extends StatefulWidget {
const PushNotificationsHandler({Key? key, required this.child})
: super(key: key);
final Widget child;
#override
_PushNotificationsHandlerState createState() =>
_PushNotificationsHandlerState();
}
class _PushNotificationsHandlerState extends State<PushNotificationsHandler> {
bool _loading = false;
Future handleOpenedPushNotification() async {
if (isWeb) {
return;
}
final notification = await FirebaseMessaging.instance.getInitialMessage();
if (notification != null) {
await _handlePushNotification(notification);
}
FirebaseMessaging.onMessageOpenedApp.listen(_handlePushNotification);
}
Future _handlePushNotification(RemoteMessage message) async {
if (_handledMessageIds.contains(message.messageId)) {
return;
}
_handledMessageIds.add(message.messageId);
if (mounted) {
setState(() => _loading = true);
}
try {
final initialPageName = message.data['initialPageName'] as String;
final initialParameterData = getInitialParameterData(message.data);
final pageBuilder = pageBuilderMap[initialPageName];
if (pageBuilder != null) {
final page = await pageBuilder(initialParameterData);
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => page),
);
}
} catch (e) {
print('Error: $e');
} finally {
if (mounted) {
setState(() => _loading = false);
}
}
}
#override
void initState() {
super.initState();
handleOpenedPushNotification();
}
#override
Widget build(BuildContext context) => _loading
? Container(
color: FlutterFlowTheme.of(context).primaryBtnText,
child: Image.asset(
'assets/images/Screen_Shot_2022-11-22_at_12.59.57.png',
fit: BoxFit.contain,
),
)
: widget.child;
}
final pageBuilderMap = <String, Future<Widget> Function(Map<String, dynamic>)>{
'SignInSignUpPage': (data) async => SignInSignUpPageWidget(),
'ForgotPasswordPage': (data) async => ForgotPasswordPageWidget(),
'ContactsListPage': (data) async => ContactsListPageWidget(
tabToOpen: getParameter(data, 'tabToOpen'),
),
'SyncProcessPage': (data) async => SyncProcessPageWidget(),
'SyncConfirmationPage': (data) async => SyncConfirmationPageWidget(),
'ContactDetailsPage': (data) async => ContactDetailsPageWidget(
idReference: getParameter(data, 'idReference'),
comment: getParameter(data, 'comment'),
),
'ContactDeletePage': (data) async => ContactDeletePageWidget(
idReference: getParameter(data, 'idReference'),
comment: getParameter(data, 'comment'),
),
'ContactInvitationPage': (data) async => ContactInvitationPageWidget(
idReference: getParameter(data, 'idReference'),
),
'AddConfirmationAfterInvitePage': (data) async =>
AddConfirmationAfterInvitePageWidget(
uID: getParameter(data, 'uID'),
phraseState: getParameter(data, 'phraseState'),
),
'AddContactsOptionPage': (data) async => AddContactsOptionPageWidget(),
'SearchNewContactsPage': (data) async => SearchNewContactsPageWidget(),
'NewContactInvitationConfirmationPage': (data) async =>
NewContactInvitationConfirmationPageWidget(),
'NewContactScanPage': (data) async => NewContactScanPageWidget(),
'AddConfirmationAfterScanPageNew': (data) async =>
AddConfirmationAfterScanPageNewWidget(
uID: getParameter(data, 'uID'),
phraseState: getParameter(data, 'phraseState'),
),
'AddConfirmationAfterScanPageOld': (data) async =>
AddConfirmationAfterScanPageOldWidget(
uID: getParameter(data, 'uID'),
phraseState: getParameter(data, 'phraseState'),
),
'ShareProfileOptionsPage': (data) async => ShareProfileOptionsPageWidget(),
'InAppQRcodeSharePage': (data) async => InAppQRcodeSharePageWidget(),
'WebQRcodeSharePage': (data) async => WebQRcodeSharePageWidget(),
'ProfileEditPage': (data) async => ProfileEditPageWidget(),
'GameSettingsPage': (data) async => GameSettingsPageWidget(),
'GamePage': (data) async => GamePageWidget(),
'TutorialPage': (data) async => TutorialPageWidget(),
'AboutPage': (data) async => AboutPageWidget(),
'FAQPage': (data) async => FAQPageWidget(),
'PrivacyPolicyPage': (data) async => PrivacyPolicyPageWidget(),
};
bool hasMatchingParameters(Map<String, dynamic> data, Set<String> params) =>
params.any((param) => getParameter(data, param) != null);
Map<String, dynamic> getInitialParameterData(Map<String, dynamic> data) {
try {
final parameterDataStr = data['parameterData'];
if (parameterDataStr == null ||
parameterDataStr is! String ||
parameterDataStr.isEmpty) {
return {};
}
return jsonDecode(parameterDataStr) as Map<String, dynamic>;
} catch (e) {
print('Error parsing parameter data: $e');
return {};
}
}
Would be super grateful for any advice!
my test is throwing an exception because there is a StateNotifierProvider inside which is not overridden. for a regular Provider, i can override it using providerContainer, but for the state of a stateNotifierProvider, I don't know how to do it. I tried my best but I reached the limit of my best. I already saw this and this but it didn't help.
Appreciate much if someone could help me out of this. Thanks
My service File
class ReportService {
final Ref ref;
ReportService({
required this.ref,
});
Future<void> testReport() async {
//* How can i override this provider ?
final connection = ref.read(connectivityServiceProvider);
if (connection) {
try {
await ref.read(reportRepositoryProvider).testFunction();
} on FirebaseException catch (e, st) {
ref.read(errorLoggerProvider).logError(e, st);
throw Exception(e.message);
}
} else {
throw Exception('Check internet connection...');
}
}
}
final reportServiceProvider = Provider<ReportService>((ref) => ReportService(
ref: ref,
));
My test file
void main() {
WidgetsFlutterBinding.ensureInitialized();
final reportRepository = MockReportRepository();
ReportService makeReportService() {
final container = ProviderContainer(overrides: [
reportRepositoryProvider.overrideWithValue(reportRepository),
]);
addTearDown(container.dispose);
return container.read(reportServiceProvider);
}
test('test test', () async {
//How to stub the connectivityServiceProvider here ?
when(reportRepository.testFunction)
.thenAnswer((invocation) => Future.value());
final service = makeReportService();
await service.testReport();
verify(reportRepository.testFunction).called(1);
});
My StateNotifierProvider
class ConnectivityService extends StateNotifier<bool> {
ConnectivityService() : super(false);
}
final connectivityServiceProvider =
StateNotifierProvider<ConnectivityService, bool>(
(ref) => ConnectivityService());
This is My Repository
class DB {
final db = FirebaseFirestore.instance;
Stream<QuerySnapshot> init(UserModel user) {
return db
.collection('CollectionName')
.doc(user.email) //this is a unique value which i want to retrieve the value from main after successful login
.collection('New Collection')
.snapshots();
}
void readData(String id, UserModel user) async {
DocumentSnapshot snapshot = await db
.collection('Collection Name')
.doc(user.email)
.collection('New Collection')
.doc(id)
.get();
// ignore: avoid_print
print(snapshot['name']);
}
}
DB db = DB();
This is My BlocFile
class IncidentBloc implements BlocBase {
IncidentBloc(UserModel user) {
db.init(user).listen((data) => _inFirestore.add(data));
}
final _idController = BehaviorSubject<String>();
Stream<String> get outId => _idController.stream;
Sink<String> get _inId => _idController.sink;
final _firestoreController = BehaviorSubject<QuerySnapshot>();
Stream<QuerySnapshot> get outFirestore => _firestoreController.stream;
Sink<QuerySnapshot> get _inFirestore => _firestoreController.sink;
void readData(UserModel user) async {
db.readData(id, user);
}
#override
void dispose() {
_firestoreController.close();
_idController.close();
}
}
And This is my main
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
BlocOverrides.runZoned(
() => runApp(
BlocProviderr(bloc: IncidentBloc(UserModel()), child: const App())
),
blocObserver: AppBlocObserver(),
);
}
It seems that the UserModel is null or empty how do i pass value to my IncidentBloc? And this is after a successful login/authentication. If I do it like this in main: "IncidentBloc(UserModel(email: 'abcde.t#gmail.com'))" It is working, but i want it to dynamically retrieve data based on the user's email not the hardcoded 'abcde.t#gmail.com'
Based on your code, you will need to get the user's email from Firebase and pass it into Incident Bloc. This StackOverflow answer explains how to do that; so does this one.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
<FirebaseUser> user = await _auth.currentUser();
final mailID = user.email;
BlocOverrides.runZoned(
() => runApp(
BlocProviderr(bloc: IncidentBloc(UserModel(email: mailID)), child: const App())
),
blocObserver: AppBlocObserver(),
);
}
I'm getting the below error while I'm trying to implement bloc testing in my flutter project
type 'Null' is not a subtype of type 'Future<bool>'
package:mynovatium/features/signup/repositories/signup_repository.dart 10:16 MockRepository.createAccountsignup
Following are the corresponding files that might help identify the cause of the error
signup_bloc_test.dart
class MockRepository extends Mock implements SignUpRepository {}
void main() async {
await configureInjection(inj.Environment.test);
group('SignupBloc', () {
late SignUpBloc signUpBloc;
late SignUpRepository signupRepositoryMock;
setUp(() {
signupRepositoryMock = MockRepository();
signUpBloc = SignUpBloc(signUpRepository: signupRepositoryMock);
});
test('initial state of the bloc is [AuthenticationInitial]', () {
expect(SignUpBloc(signUpRepository: signupRepositoryMock).state,
SignupInitial(),);
});
group('SignUpCreateAccount', () {
blocTest<SignUpBloc, SignUpState>(
'emits [SignUpCreateAccountLoading, SignupInitial] '
'state when successfully Signed up',
setUp: () {
when(signupRepositoryMock.createAccount(
'Nevil',
'abcd',
'nikunj#gmail.com',
'english',
),).thenAnswer((_) async => Future<bool>.value(true));
},
build: () => SignUpBloc(signUpRepository: signupRepositoryMock),
act: (SignUpBloc bloc) => bloc.add(
const SignUpCreateAccount(
'Nevil',
'abcd',
'nikunj#gmail.com',
'english',
),
),
expect: () => [
SignUpCreateAccountLoading(),
SignupInitial(),
],
);
});
});
}
signup_repository.dart
This is the code for the signup repository.
class SignUpRepository {
Future<bool> createAccount(String _firstName, String _lastName, String _eMailAddress, String _language) async {
final Response _response;
try {
_response = await CEApiRequest().post(
Endpoints.createCustomerAPI,
jsonData: <String, dynamic>{
'firstName': _firstName,
'lastName': _lastName,
'email': _eMailAddress,
'language': _language,
'responseUrl': Endpoints.flutterAddress,
},
);
final Map<String, dynamic> _customerMap = jsonDecode(_response.body);
final CustomerModel _clients = CustomerModel.fromJson(_customerMap['data']);
if (_clients.id != null) {
return true;
} else {
return false;
}
} on KBMException catch (e) {
final KBMException _exception = e;
throw _exception;
}
}
}
If anyone has any ideas on what might be the issue here, please help!!
Okay so in the above code you need to stub the methods within the mock repository as well and override it to have it return something incase null is being returned.
class MockRepository extends Mock implements SignUpRepository {
#override
Future<bool> createAccount(String? _firstName, String? _lastName, String? _eMailAddress, String? _language) =>
super.noSuchMethod(Invocation.method(#createAccount, [_firstName, _lastName, _eMailAddress, _language]),
returnValue: Future<bool>.value(false),);
}
Doing something like that done in the above code works well.
I am trying to learn bloc and I am writing simple unit tests. I am trying to test the auth events but I am facing the error below. Inside my app when I trigger an event, I don't get any errors and everything seems to work fine, so why am I getting error here? Am I missing something, could anyone advise?
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
final AuthenticationRepository _authRepository;
late StreamSubscription<AuthStatus> _authSubscription;
AuthenticationBloc(
{required AuthenticationRepository authenticationRepository})
: _authRepository = authenticationRepository,
super(const AuthenticationState()) {
on<AuthStateChanged>(_onAuthStatusChanged);
on<AuthenticationLogoutRequested>(_onLogoutRequested);
_authSubscription = _authRepository.status
.listen((status) => add(AuthStateChanged(authStatus: status)));
}
enum AuthStatus { unknown, authenticated, unauthenticated }
class AuthenticationRepository {
final _controller = StreamController<AuthStatus>();
Stream<AuthStatus> get status => _controller.stream;
Future<void> logIn({
required String username,
required String password,
}) async {
await Future.delayed(
const Duration(milliseconds: 300),
() => _controller.add(AuthStatus.authenticated),
);
}
void logOut() {
_controller.add(AuthStatus.unauthenticated);
}
void dispose() => _controller.close();
}
class AuthenticationState extends Equatable {
final AuthStatus status;
final User? user;
const AuthenticationState({this.status = AuthStatus.unknown, this.user});
#override
List<Object?> get props => [user, status];
}
void main() {
late AuthenticationBloc authenticationBloc;
MockAuthenticationRepository authenticationRepository = MockAuthenticationRepository();
setUp((){
authenticationBloc = AuthenticationBloc(authenticationRepository: authenticationRepository);
});
group('AuthenticationEvent', () {
group('Auth status changes', () {
test('User is unknown', () {
expect(authenticationBloc.state.status, AuthStatus.unknown);
});
test('User is authorized', () {
authenticationBloc.add(AuthStateChanged(authStatus: AuthStatus.authenticated));
expect(authenticationBloc.state.status, AuthStatus.authenticated);
});
test('User is unauthorized', () {
authenticationBloc.add(AuthStateChanged(authStatus: AuthStatus.unauthenticated));
expect(authenticationBloc.state.status, AuthStatus.unknown);
});
});
});
}
It is most likely that you have not created a stub for a function in your mock class. This is generally the cause of the NULL return. This can be achieved by using when() from the mockito package. https://pub.dev/packages/mockito.
Also there is a bloc testing package that may be useful to look into. https://pub.dev/packages/bloc_test
Below is an example of how I implemented it. Hope this helps.
blocTest<ValidateUserBloc, ValidateUserState>('Validate User - Success',
build: () {
when(mockSettingsRepo.validateUser(url, authCode)).thenAnswer(
(_) async => User(
success: true, valid: true, url: url, authCode: authCode));
return validateUserBloc;
},
seed: () => ValidateUserState(user: User(url: url, authCode: authCode)),
act: (bloc) => bloc.add(ValidateUserAuthoriseEvent()),
expect: () => <ValidateUserState>[
ValidateUserState(
status: ValidUserStatus.success,
user: User(
success: true, valid: true, url: url, authCode: authCode))
]);