I'm brand new to flutter and I want to try to make a Unit Test for one functions that I've created. The function simply test if the email & password that the user provides to login are correct or not. So, the function provides a connection to the database and verify if the email & password are valid.
I tried to make a Unit Test with mockito to emulate the Firebase but it don't work very well. My function return an "UserCredential" type and I don't know how to verify it with Mocks. I tried to create a Mock for this type, but it says that "type 'Null' is not a subtype of type 'Future'"...
Anyone can make a clear explanation ? :)
Firebase Function to test
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flexmes/data/models/user_model.dart';
class UserAPI{
final CustomUser _customUser = CustomUser();
final FirebaseAuth auth;
UserAPI({required this.auth});
Future<UserCredential?> signInWithEmailAndPassword(String email, String password) async {
try{
UserCredential result = await auth.signInWithEmailAndPassword(email: email, password: password);
print (result);
return result;
} catch (error){
print (error);
return null;
}
}
}
Unit Test for the signInWithEmailAndPassword function
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flexmes/data/data_providers/user_provider.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
class MockFirebaseAuth extends Mock implements FirebaseAuth{}
class MockUserCredential extends Mock implements UserCredential{}
void main(){
final MockFirebaseAuth mockAuth = MockFirebaseAuth();
final MockUserCredential userCredential = MockUserCredential();
group('description', () {
late UserAPI userAPI;
setUp(() {
userAPI = UserAPI(auth: mockAuth);
});
tearDown(() {
});
test('Sign in with Email & Password', () async {
when(mockAuth.signInWithEmailAndPassword(email: "admin#admin.com", password: "password")).
thenAnswer((_) => Future<MockUserCredential>.value(userCredential));
expect(await userAPI.signInWithEmailAndPassword("admin#admin.com", "password"), userCredential);
});
});
}
There is mock authentication package available firebase_auth_mocks and google_sign_in_mocks
You can replace your code like this
group('description', () {
final _mockAuth = MockFirebaseAuth(mockUser: _mockUser);
late UserAPI userAPI;
setUp(() {
userAPI = UserAPI(auth: _mockAuth);
});
tearDown(() {});
const _email = 'ilyas#yopmail.com';
const _uid = 'sampleUid';
const _displayName = 'ilyas';
const _password = 'Test#123';
final _mockUser = MockUser(
uid: _uid,
email: _email,
displayName: _displayName,
);
test('signIn function test', () async {
final user = await userAPI.signInWithEmailAndPassword(_email, _password);
expect(user?.uid, _mockUser.uid);
});
});
google_sign_in_mocks this you can use for GoogleSignIn test
Related
I need the username and password that is created to be saved in the database, I have already created my database with realtime database that I am currently using to store data from a form that I create
import 'package:firebase_auth/firebase_auth.dart';
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
abstract class BaseAuth {
Future<String> singInWithEmailAndPassword(String email, String password);
Future<String> createUserWithEmailAndPassword(String email, String password);
Future<String> currentUser();
Future<void> singOut();
}
class Auth implements BaseAuth {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final databaseRef = FirebaseDatabase.instance.ref();
#override
Future<String> singInWithEmailAndPassword(
String email, String password) async {
UserCredential user = await _firebaseAuth.signInWithEmailAndPassword(
email: email.toString(), password: password.toString());
return user.toString();
}
#override
Future<String> createUserWithEmailAndPassword(
String email, String password) async {
UserCredential user = await _firebaseAuth.createUserWithEmailAndPassword(
email: email.toString(), password: password.toString());
return user.toString();
}
#override
Future<String> currentUser() async {
User currentUser = _firebaseAuth.currentUser!;
return currentUser.toString();
}
#override
Future<void> singOut() async {
return _firebaseAuth.signOut();
}
}
This es my auth.dart,this is where i created my methods to create and login
I hope you can support me since this is part of my final project
I'm learing test writing with mockito but i have some problems with testing remote_data_source_class.
Here's my abstract class
abstract class ApiRemoteDataSource {
Future<PokemonsListResponseModel> getPokemons();
}
Here's the implementation
import 'dart:convert';
import 'package:injectable/injectable.dart';
import 'package:http/http.dart' as http;
import '../../models/pokemons_list_response_model.dart';
import 'api_remote_data_source.dart';
#Injectable(as: ApiRemoteDataSource)
class ApiRemoteDataSourceImpl implements ApiRemoteDataSource {
ApiRemoteDataSourceImpl(this.client);
final http.Client client;
final pokemonListUrl = Uri.parse('https://pokeapi.co/api/v2/pokemon');
#override
Future<PokemonsListResponseModel> getPokemons() async {
final response = await client.get(pokemonListUrl);
final data = response.body;
final modelData = PokemonsListResponseModel.fromJson(json.decode(data));
return modelData;
}
}
Now Iwant to verify that when i invoke method .getPokemons() on mock data source my http.Client will execute a call to given endpoint:
#GenerateMocks([http.Client, ApiRemoteDataSource])
void main() {
late ApiRemoteDataSource dataSource;
late MockClient mockHttpClient;
setUp(() async {
await configureDependencies();
mockHttpClient = MockClient();
dataSource = MockApiRemoteDataSource();
print('test: $mockHttpClient, data source $dataSource');
});
group('getPokemonsList', () {
test('Should perform a GET request on a URL', () async {
final url = Uri.parse('https://pokeapi.co/api/v2/pokemon');
// arrange
when(mockHttpClient.get(url, headers: anyNamed('headers')))
.thenAnswer((_) async => http.Response(fixture('pokemon_list.json'), 200));
// act
dataSource.getPokemons();
// assert
verify(mockHttpClient.get(Uri.parse('https://pokeapi.co/api/v2/pokemon')));
});
});
}
Running test above gives me this error: "MissingStubError: 'getPokemons'
No stub was found which matches the arguments of this method call:
getPokemons()"
When i replace dataSource.getPokemons() in //act part with "mockHttpClient.get(url)" everything works but I'm not sure if that kind of test is valid
I'm trying to test my whole AuthManager class which use FirebaseAuth to signin, login. Here my file:
class AuthManager extends ChangeNotifier {
final FirebaseAuth auth;
Stream<User?> get user => auth.authStateChanges();
static Future<FirebaseApp> initializeFirebase({
required BuildContext context,
}) async {
FirebaseApp firebaseApp = await Firebase.initializeApp();
return firebaseApp;
}
AuthManager({required this.auth});
Future<String> signup(String email, String password) async {
try {
final credential = await auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
return "Success";
} on FirebaseAuthException catch (e) {
rethrow;
}
}
Future<String> signInWithEmailAndPassword(
String email, String password) async {
try {
final userCredential = await auth.signInWithEmailAndPassword(
email: email, password: password);
return "Success";
} on FirebaseAuthException catch (e) {
return "Failed";
} catch (e) {
rethrow;
}
}
static Future<String> signOut() async {
try {
await FirebaseAuth.instance.signOut();
return "Success";
} catch (e) {
rethrow;
}
}
}
I used to return the usercredential but wanted to try test a simple string return for the test, following this tutorial: https://www.youtube.com/watch?v=4d6hEaUVvuU, here is my test file
import 'package:firebase_auth/firebase_auth.dart';
import 'package:notes_firebase_app/data/models/auth_manager.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
class MockFirebaseAuth extends Mock implements FirebaseAuth {
#override
Stream<User> authStateChanges() {
return Stream.fromIterable([
_mockUser,
]);
}
}
class MockUser extends Mock implements User {}
final MockUser _mockUser = MockUser();
class MockUserCredential extends Mock implements Future<UserCredential> {}
void main() {
final MockFirebaseAuth mockFirebaseAuth = MockFirebaseAuth();
final AuthManager authManager = AuthManager(auth: mockFirebaseAuth);
final MockUserCredential mockUserCredential = MockUserCredential();
setUp(() {});
test("emit occurs", () async {
expectLater(authManager.user, emitsInOrder([_mockUser]));
});
test("create account", () async {
when(mockFirebaseAuth.createUserWithEmailAndPassword(
email: "tadas#gmail.com", password: "123456"))
.thenAnswer((realInvocation) => null);
expect(
await authManager.signInWithEmailAndPassword(
"tadas#gmail.com", "123456"),
"Success");
});
}
I face two problems here, cannot pass null because we need to handle it now or this throw this error
The return type 'Null' isn't a 'Future<UserCredential>', as required by the closure's context
Then I tried to mock UserCredential like this.
final MockUserCredential mockUserCredential = MockUserCredential();
when(mockFirebaseAuth.createUserWithEmailAndPassword(
email: "tadas#gmail.com", password: "123456"))
.thenAnswer((realInvocation) => mockUserCredential);
but I'm getting this error:
type 'Null' is not a subtype of type 'Future<UserCredential>'
What am I doing wrong ? Help will be much appreciated.
I am not totally sure but mockito package may need a generator. Try mocktail package and use
when(()=> mockFirebaseAuth.createUserWithEmailAndPassword(
email: "tadas#gmail.com", password: "123456")).thenAnswer((realInvocation) => null);
use callback function in when().
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
dependencies {
// Import the Firebase BoM
implementation platform('com.google.firebase:firebase-bom:29.0.1')
// Add the dependency for the Firebase SDK for Google Analytics
// When using the BoM, don't specify versions in Firebase dependencies
implementation 'com.google.firebase:firebase-analytics'
// Add the dependencies for any other desired Firebase products
// https://firebase.google.com/docs/android/setup#available-libraries
implementation 'com.facebook.android:facebook-android-sdk:latest.release'
}
}
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
abstract class AuthBase{
User get currentUser;
Stream<User> authStateChanges();
Future<User>signInAnonymously();
Future<User> signInWithGoogle();
Future<void>signOut();
}
class Auth implements AuthBase {
final _firebaseAuth = FirebaseAuth.instance;
Stream<User> authStateChanges() => _firebaseAuth.authStateChanges();
#override
User get currentUser => _firebaseAuth.currentUser;
#override
Future<User> signInAnonymously() async {
final userCredential = await _firebaseAuth.signInAnonymously();
return userCredential.user;
}
#override
Future<User> signInWithGoogle() async{
final googleSignIn = GoogleSignIn();
final googleUser = await googleSignIn.signIn();
if(googleUser !=null) {
final googleAuth = await googleUser.authentication;
if (googleAuth.idToken != null) {
final userCredential = await _firebaseAuth.signInWithCredential(
GoogleAuthProvider.credential(
idToken: googleAuth.idToken,
accessToken: googleAuth.accessToken,
));
return userCredential.user;
} else {
throw FirebaseAuthException(
code:'ERROR_MISSING_GOOGLE_ID_TOKEN',
message:'Missing Google ID Token',
);
}
}
else {
throw FirebaseAuthException(
code: 'ERROR_ABORTED_BY_USER',
message: 'Sign in aborted by user'
);
}
}
Future<User> signInWithFacebook() async{
final fb = FacebookLogin();
}
#override
Future<void> signOut() async {
final googleSignIn = GoogleSignIn();
await googleSignIn.signOut();
await _firebaseAuth.signOut();
// TODO: implement signOut
throw UnimplementedError();
}
}
`]1][1][I'm trying to implement facebook SDK into my project.I have followed all of the steps up to this point and you can find the link below but I'm still getting an error When I copied this code in to the Main activity page.What am I doing wrong?I posted my auth.dart file and the dependencies as well
package com.example.time_trackerpractice
import io.flutter.embedding.android.FlutterActivity
import com.facebook.FacebookSdk;
import com.facebook.appevents.AppEventsLogger;
class MainActivity: FlutterActivity() {
}
The problem that you have (see image) its because you don't have any flutter packages related to facebook auth.
In this line of your code you are try instance a FacebookLogin, but doesn't exits any import related to facebook login.
final fb = FacebookLogin();
You can use this package to facebook login (flutter_facebook_auth), and replace your inside code of your function signInWithFacebook for this:
Future<User> signInWithFacebook() async {
final result = await FacebookAuth.instance
.login(permissions: ['email', 'public_profile']);
final accessToken = result.accessToken;
final credential = FacebookAuthProvider.credential(accessToken.token);
userCredential = await _firebaseAuth.signInWithCredential(credential);
return userCredential.user
}
I have the following repository and I'd like to test it. I know this may be a silly question but I'm still learning.
class AuthRepository implements AuthBaseRepository {
final Reader _read;
const AuthRepository(this._read);
#override
Future<User> login({String email, String password}) async {
try {
final response = await _read(dioProvider).post(
'/sign_in',
data: {
"user": {
"email": email,
"password": password,
},
},
);
return _mapUserFromResponse(response);
} on DioError catch (_) {
throw const CustomException(message: 'Invalid login credentials.');
} on SocketException catch (_) {
const message = 'Please check your connection.';
throw const CustomException(message: message);
}
}
And this is what I've done so far:
void main() {
test('loadUser', () async {
Dio dio;
DioAdapterMockito dioAdapterMockito;
AuthRepository repository;
setUpAll(() {
dio = Dio();
dioAdapterMockito = DioAdapterMockito();
dio.httpClientAdapter = dioAdapterMockito;
repository = AuthRepository(_reader_here_);
});
test('mocks any request/response via fetch method', () async {
final responsePayload =
await parseJsonFromAssets("assets/api-response.json");
final responseBody = ResponseBody.fromString(
responsePayload,
200,
headers: {
Headers.contentTypeHeader: [Headers.jsonContentType],
},
);
when(dioAdapterMockito.fetch(any, any, any))
.thenAnswer((_) async => responseBody);
});
});
}
I have no idea of how to mock Reader. Basically, I've seen something like class MyMock extends Mock implements Something but Reader is not a class, it's a function so I'm completely lost.
Any help/tips/examples will be appreciated.
Thanks in advance!
Instead of trying to mock a Reader, create a provider for your repository and use ProviderContainer to read it.
class AuthRepository implements AuthBaseRepository {
const AuthRepository(this._read);
static final provider = Provider<AuthRepository>((ref) => AuthRepository(ref.read));
final Reader _read;
#override
Future<User> login({String email, String password}) async {
...
}
Example usage:
final user = createTestUser();
final container = ProviderContainer(
overrides: [
// Example of how you can mock providers
dio.overrideWithProvider(mockDio),
],
);
final repo = container.read(AuthRepository.provider);
expectLater(
await repo.login(email: 'AzureDiamond', password: 'hunter2'),
user,
);
You could also consider using the overrides in ProviderContainer to mock Dio instead of involving a mocking framework to simplify your tests further.
More on testing here.