How to setup blocTest for a feature that requires authentication? - flutter

So I'm implementing blocTesting for my flutter project and I'm using real apis in testing as that is what I've been asked to do. Apparently when I'm trying out blocTesting for a feature that requires authentication the api is giving back a null response but in the running app it is working fine...
I'm adding the respective files below for the same. Also the name of feature is client and I'm trying to fetch all clients here.
Client_bloc_test.dart
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:injectable/injectable.dart' as inject;
import 'package:mynovatium/app/helper/injections/shared_preference/shared_preference_injection_configuration.dart';
import 'package:mynovatium/features/buy/models/test_model.dart';
import 'package:mynovatium/features/client/bloc/client_bloc.dart';
import 'package:mynovatium/features/client/models/private_client_model.dart';
import 'package:mynovatium/features/login/bloc/authentication/authentication_bloc.dart';
import 'package:mynovatium/features/login/repositories/authentication_repository.dart';
void main() async {
await configureInjection(inject.Environment.test);
final AuthenticationBloc authenticationBloc = AuthenticationBloc();
group('ClientBloc', () {
late PrivateClientModel client;
test('initial state of the bloc is [SignupInitial]', () {
expect(
ClientBloc(authenticationBloc: authenticationBloc).state,
ClientInitial(),
);
});
group('ClientCreateClient', () {
blocTest<ClientBloc, ClientState>(
'emits [ClientLoadInProgress, ClientLoadSuccess] '
'state when successfully fetched clients',
setUp: () async {
client = PrivateClientModel(1, List<TestModel>.empty(), createdAt: DateTime.now(), gender: 'Male', firstName: 'Nikunj', lastName: 'goyal', birthDate: '03/07/2001', ssnr: '0000', email: 'nik#gmail.com', telephoneNumber: '6641234123', street: 'Abcd', zipCode: '6020', city: 'Tyrol');
},
build: () => ClientBloc(authenticationBloc: authenticationBloc),
act: (ClientBloc bloc) => bloc..add(const ClientGetClients(101010)),
expect: () => <dynamic>[
const ClientLoadInProgress(),
],
);
});
});
}
Client_bloc.dart
Future<void> _onGetClients(ClientGetClients event, Emitter<ClientState> emit) async {
// Skip double loadings
if (state is ClientLoadInProgress) return;
PrivateClientModelList _pcml = PrivateClientModelList(<PrivateClientModel>[]);
final PrivateRedeemModel _prm = PrivateRedeemModel(customerId: event.userId, clients: <RedeemClient>[]);
if (state is ClientLoadSuccess) {
final ClientLoadSuccess _currentState = state as ClientLoadSuccess;
_pcml = _currentState.pcml;
try {
emit(const ClientLoadInProgress());
_pcml = await _clientRepository.getClientsForUser(event.userId);
} catch (e) {
final KBMException _exception = e as KBMException;
emit(ClientLoadFailure(exception: _exception));
}
return emit(_currentState.copyWith(pcml: _pcml));
} else {
try {
emit(const ClientLoadInProgress());
_pcml = await _clientRepository.getClientsForUser(event.userId);
for (final PrivateClientModel _client in _pcml.data) {
_prm.clients.add(RedeemClient(clientId: _client.id, quantity: 0));
}
} catch (e) {
final KBMException _exception = e as KBMException;
emit(ClientLoadFailure(exception: _exception));
}
return emit(ClientLoadSuccess(_pcml, _prm));
}
}
In the above code when 'getClientsForUser' is being called the response is null.

Related

Repository testing with mocktail and flutter test - I'm trying to write a test for my repository but I keep getting an error 'No such mrthod'

error message Repository testing with mocktail and flutter test - I'm trying to write a test for my repository but I keep getting an error 'No such method'
I've tried with ThenAnswer as well but it still won't go through. I'll appreciate any help :)
That's what my code looks like :
Test file:
`import 'package:curlzzz_new/data_source/remote_watch_data_source.dart';
import 'package:curlzzz_new/models/watch_model.dart';
import 'package:curlzzz_new/repositories/watch_repository.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
class MockWatchDataSource extends Mock implements RemoteWatchDataSource {}
void main() {
//sut to System under testing
late WatchRepository sut;
late MockWatchDataSource dataSource;
setUp(() {
dataSource = MockWatchDataSource();
sut = WatchRepository(dataSource);
});
group('getWatchStream', () {
test('should return a stream of WatchModels', () {
//1
when(() => dataSource.getRemoteWatchStream()).thenThrow(
(_) => Stream.value([
WatchModel(title: 'titanic', id: '555'),
WatchModel(title: 'top gun', id: '555'),
WatchModel(title: 'matrix', id: '111')
]),
);
//2
final results = sut.getWatchStream();
//3
expect(results, [
WatchModel(title: 'titanic', id: '555'),
WatchModel(title: 'top gun', id: '555'),
WatchModel(title: 'matrix', id: '111')
]);
});
});
}
`
Repository:
import 'package:curlzzz_new/data_source/remote_watch_data_source.dart';
import 'package:curlzzz_new/models/watch_model.dart';
class WatchRepository {
WatchRepository(this.remoteDataSource);
final RemoteWatchDataSource remoteDataSource;
Stream<List<WatchModel>> getWatchStream() {
return remoteDataSource.getRemoteWatchStream().map((querySnapshot) {
return querySnapshot.docs.map((doc) {
return WatchModel(title: doc['title'], id: doc.id);
}).toList();
});
}
Future<void> addMovies({
required String title,
}) {
return remoteDataSource.addRemoteWatchData(title: title);
}
Future<void> dismiss({required String id}) {
return remoteDataSource.dismissRemoteData(id: id);
}
}
Data Source:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
class RemoteWatchDataSource {
Stream<QuerySnapshot<Map<String, dynamic>>> getRemoteWatchStream() {
final userID = FirebaseAuth.instance.currentUser?.uid;
if (userID == null) {
throw Exception('User is not logged in');
}
return FirebaseFirestore.instance
.collection('users')
.doc(userID)
.collection('movies')
.snapshots();
}
Future<DocumentReference<Map<String, dynamic>>?> addRemoteWatchData({
required String title,
}) async {
final userID = FirebaseAuth.instance.currentUser?.uid;
if (userID == null) {
throw Exception('User is not logged in');
}
return FirebaseFirestore.instance
.collection('users')
.doc(userID)
.collection('movies')
.add(
{'title': title},
);
}
Future<void> dismissRemoteData({required String id}) async {
final userID = FirebaseAuth.instance.currentUser?.uid;
if (userID == null) {
throw Exception('User is not logged in');
}
return FirebaseFirestore.instance
.collection('users')
.doc(userID)
.collection(
'movies',
)
.doc(id)
.delete();
}
}
It seems like a a problem with the closure type.
try this. don't combine 2 functions in a single test.
group('getWatchStream', () {
test('should return a stream of WatchModels', () async {
final List<WatchModel> models = [
WatchModel(title: 'titanic', id: '555'),
WatchModel(title: 'top gun', id: '555'),
WatchModel(title: 'matrix', id: '111')
];
//1
when(() => dataSource.getRemoteWatchStream()).thenAnswer(
(_) => Stream.value(models),
);
//2
await dataSource.getRemoteWatchStream();
//3
expect(dataSource.getRemoteWatchStream, emits(models) );
});
});

type 'Null' is not a subtype of type 'Future<bool>'

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.

Flutter socket io not establishing connection after logging in the app

so I'm having this really weird bug connecting to the backend ws server.
flutter 2.10.3
dart 2.16.1
socket_io_client ^2.0.0-beta.4-nullsafety.0
If I start the app, and go through the login process, it doesn't connect, doesn't show any error, or any event. there's nothing at all. (but it sometimes works, it's totally random but let's say it does 1/10 times)
And if I reload the app, it connects successfully. also when I add breakpoints in the code, it works fine.
// ignore_for_file: avoid_print, cascade_invocations
import 'package:bloc/bloc.dart';
import 'package:dealize/core/helpers/flutter_secure_storage_helper.dart';
import 'package:dealize/features/chat/data/models/channel_model.dart';
import 'package:dealize/features/chat/data/models/message_model.dart';
import 'package:dealize/features/chat/domain/entities/channel.dart';
import 'package:dealize/features/chat/domain/entities/message.dart';
import 'package:equatable/equatable.dart';
import 'package:socket_io_client/socket_io_client.dart' as io;
part 'chat_event.dart';
part 'chat_state.dart';
class ChatBloc extends Bloc<ChatEvent, ChatState> {
ChatBloc({
required this.socketUrl,
required this.secureStorageHelper,
}) : super(
const ChatState(
channels: <Channel>[],
messages: <Message?>[],
),
) {
on<Connect>(
(event, emit) async {
try {
final accessToken = await secureStorageHelper.read('accessToken');
_socket = io.io(
socketUrl,
io.OptionBuilder()
.setTransports(['websocket'])
.setAuth(
<String, dynamic>{
'token': accessToken,
},
)
.disableAutoConnect()
.build(),
);
_socket
..onAny((event, dynamic data) {
print('EVENT ===> $event');
print('DATA ===> $data');
})
..onDisconnect((dynamic data) {
print('Disconnected: $data');
})
..on('channel', (dynamic data) async {
final channel =
ChannelModel.fromJson(data as Map<String, dynamic>);
add(AddedChannel(channel: channel));
})
..on('participant', (dynamic data) async {
print('Participant ===> $data');
// todo find participant and update it
// todo add updatedChannel event
})
..on('message', (dynamic data) async {
final message =
MessageModel.fromJson(data as Map<String, dynamic>);
add(AddedMessage(message: message));
add(MessageDelivered());
})
..on('exception', (dynamic data) async {
print('Error ===> $data');
// todo show toast message
});
_socket.connect();
} catch (e) {
print(e.toString());
}
},
);
on<AddedChannel>(
(event, emit) =>
emit(state.copyWith(channels: [...state.channels, event.channel])),
);
on<AddedMessage>(
(event, emit) {
final currentChannelIndex = state.channels.indexWhere(
(channel) => channel.id == event.message.channel,
);
final currentChannel = state.channels.removeAt(currentChannelIndex);
final messageIndex = currentChannel.messages
.indexWhere((element) => element!.id == event.message.id);
if (messageIndex == -1) {
currentChannel.messages.add(event.message);
} else {
currentChannel.messages[messageIndex] = event.message;
}
emit(
state.copyWith(
channels: [...state.channels, currentChannel],
),
);
},
);
on<CreateChannel>((event, emit) {
final phone = '+${state.dialCode}${state.phone}';
_socket.emit('create_channel', {
'phone': phone,
});
});
on<CreateMessage>((event, emit) {
_socket.emit('create_message', {
'channelId': event.channelId,
'type': event.type.name,
'text': state.newMessage,
});
});
on<MessageDelivered>((event, emit) {
_socket.emit('message_delivered', {
'': '',
});
});
on<MessageRead>((event, emit) {
_socket.emit('message_read', {
'': '',
});
});
on<PhoneNumberUpdated>(
(event, emit) => emit(state.copyWith(phone: event.phone)),
);
on<DialCodeUpdated>(
(event, emit) => emit(state.copyWith(dialCode: event.dialCode)),
);
on<NewMessageUpdated>(
(event, emit) => emit(state.copyWith(newMessage: event.message)),
);
on<EnterChannel>((event, emit) {
emit(
state.copyWith(
channelId: event.channelId,
messages: state.channels
.firstWhere((element) => element.id == event.channelId)
.messages,
),
);
add(MessageRead());
});
on<ExitChannel>(
(event, emit) => emit(
state.copyWith(
channelId: '',
messages: [],
),
),
);
}
final String socketUrl;
final FlutterSecureStorageHelper secureStorageHelper;
late final io.Socket _socket;
}
If anyone has an idea it would be really helpful, or a better way to implement socket io connection in flutter through BLoC

How to implement authentication in flutter using riverpod

I am trying to do a login using riverpod, the login must call an api but at the moment I am using a fake api I want that while the api is called I can show a loader to the user and if everything is correct navigate to home.
I have the following:
authentication_service.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:watch_movie_app/src/data/data_source/local/fake_data.dart';
import 'package:watch_movie_app/src/data/models/models.dart';
import 'package:watch_movie_app/src/domain/models/models.dart';
class AuthenticationService {
Future<Auth> login(User user) async {
await Future.delayed(const Duration(seconds: 2));
User isName = fakeUsers.firstWhere(
(element) => element.name == user.name,
orElse: () => emptyUser(),
);
User isPassword = fakeUsers.firstWhere(
(element) => element.password == user.password,
orElse: () => emptyUser(),
);
if (isName.name != '' && isPassword.password != '') {
return Auth(
message: 'Succes welcome user',
status: true,
aditionalData: getRandomString(15));
}
return Auth(message: 'Credenciales incorrectas', status: false);
}
}
auth_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:watch_movie_app/src/data/enums/enum_login_status.dart';
import 'package:watch_movie_app/src/domain/constants/constants.dart';
import 'package:watch_movie_app/src/domain/providers/app_providers.dart';
final userAuthProvider = StateProvider<Map<String, dynamic>>((_) => {
'signedIn': false,
'loaded': false,
'status': LoginStatus.initialize,
});
final saveUserTokenProvider = StateProvider.family<bool, String>((ref, token) {
final localStore = ref.read(localStoreProvider);
localStore.write(tokenKey, token);
return true;
});
final userTokenProvider = StateProvider<String>((ref) {
final localStore = ref.read(localStoreProvider);
return localStore.read(tokenKey);
});
login_controller.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:watch_movie_app/src/data/enums/enum_login_status.dart';
import 'package:watch_movie_app/src/data/models/models.dart';
import 'package:watch_movie_app/src/domain/models/models.dart';
import 'package:watch_movie_app/src/domain/providers/auth_provider.dart';
import 'package:watch_movie_app/src/domain/services/authentication_service.dart';
final authProvider = StateNotifierProvider((ref) => AuthNotifier(ref));
class AuthNotifier extends StateNotifier<Auth> {
final WidgetRef ref;
dynamic _authRepository;
AuthNotifier(this.ref) : super(Auth()) {
_authRepository = ref.read(authRepository);
}
Future<void> login(User user) async {
Map<String, dynamic> userAuthStatus = ref.read(userAuthProvider.state).state;
userAuthStatus = {...userAuthStatus, 'loaded': true};
final Auth loginResult = await _authRepository.login(user);
state = loginResult;
if (loginResult.status) {
userAuthStatus = {'loaded': false, 'signedIn': true, 'status': LoginStatus.success};
} else {
userAuthStatus = {...userAuthStatus, 'loaded': false, 'status': LoginStatus.failed};
}
}
void clearUser() {
state = Auth();
}
}
auth.dart
class Auth {
final String message, aditionalData;
bool status;
Auth({this.message = '', this.status = false, this.aditionalData = ''});
}
versions packages:
flutter_riverpod: ^2.0.0-dev.0
Flutter 2.10.2
Dart 2.15.1 DevTools 2.9.2

Flutter : problem with creating a document in firestore with riverhooks

whenever i want to make a document in firestore it wo't let me , i think its something that i did with riverpod and with hooks
here is the code :
onPressed: () async {
currentOrganization.when(
data: (organization) async {
_post.value = _post.value
.copyWith(creator: organization);
print(_post.value);
String imageid = uuid.v4();
pickedImage = await ImagePicker()
.getImage(
source: ImageSource.gallery,
maxWidth: 1920,
maxHeight: 1200,
imageQuality: 80);
Reference reference = FirebaseStorage
.instance
.ref()
.child("$imageid");
UploadTask uploadTask = reference
.putFile(File(pickedImage.path));
await uploadTask.then((res) async {
imageUrl = await reference
.getDownloadURL();
});
_post.value = _post.value
.copyWith(picture: imageUrl);
},
loading: () =>
const CircularProgressIndicator(),
error: (error, stack) => Center(
child: Text("$error"),
));
},
),
),
GestureDetector(
onTap: () async {
context
.read(createPostUSeCaseProvider)
.execute(CreatePostUseCaseInput(
uuid.v4(),
_post.value.description,
_post.value.title,
_post.value.creator,
_post.value.likes,_post.value.picture))
.then(
(result) => result.fold(
(failure) {
print('FAILURE: $failure');
toast("Please try again");
},
(unit) {
toast(" successfully");
},
),
);
},
when i delete the currentOrganizationProvider everything works like charm
here is the
UseCase
import 'package:dartz/dartz.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:takaful/application/core/use_cases/i.use_case.dart';
import 'package:takaful/application/post/use_case/create_post/create_post.input.dart';
import 'package:takaful/application/providers/post.repository.provider.dart';
import 'package:takaful/domain/core/i.failure.dart';
import 'package:takaful/domain/model/post.dart';
import 'package:takaful/domain/post/i.post.repository.dart';
final createPostUSeCaseProvider = Provider((ref) =>
CreatePostUseCase(postRepository: ref.watch(postRepositoryProvider)));
class CreatePostUseCase implements IUseCase<CreatePostUseCaseInput, Unit> {
final IPostRepository _postRepository;
CreatePostUseCase({IPostRepository postRepository})
: _postRepository = postRepository;
#override
Future<Either<IFailure, Unit>> execute(input) async {
return await _postRepository.createPost(
description: input.discreption,
model: input.model,
id: input.id,
title: input.title,
likes: input.likes,
picture: input.picture);
}
}
and the
infrastructure code :
Future<Either<UserFailures, Unit>> createPost({String id, String title, String description, OrganizationModel model, int likes,String picture}) async{
try {
await _firestore.collection(collection).doc().set({
"id":id,
"title":title,
"description":description,
"creator":model,
"likes":likes,
"picture":picture
});
return right(unit);
} catch (error) {
return left(UserFailures.serverError());
}
}
when i delete the the provider everything works , any idea , and i need the model !