Dart - getting confused by test result - flutter

Apologies if this is a totally noob question, but I can't find a way through the following problem:
Test result:
Expected: <Instance of 'Future< SignUpResultSuccess >'>
Actual: <Instance of 'Future< SignUpResult >'>
I was expecting the actual result to be a 'Future< SignUpResultSuccess >' rather than a 'Future< SignUpResult >'.
Here's the code:
import 'package:equatable/equatable.dart';
import '../data/authentication_data_provider.dart';
abstract class SignUpResult {}
class SignUpResultSuccess extends Equatable implements SignUpResult {
const SignUpResultSuccess();
#override
List<Object?> get props => [];
}
class SignUpResultFailure extends Equatable implements SignUpResult {
const SignUpResultFailure({String? errorCode}) : _errorCode = errorCode ?? '';
final String _errorCode;
String get errorCode => _errorCode;
#override
List<Object?> get props => [_errorCode];
}
class AuthenticationRepository {
AuthenticationRepository({
AuthenticationDataProvider? authenticationDataProvider,
}) : _authenticationDataProvider =
authenticationDataProvider ?? AuthenticationDataProvider();
final AuthenticationDataProvider _authenticationDataProvider;
Future<SignUpResult> signUp({
required String email,
required String password,
}) async {
try {
await _authenticationDataProvider.createEmailAccount(
email: email,
password: password,
);
return const SignUpResultSuccess();
} on CreateEmailAccountException catch (createEmailAccountException) {
return SignUpResultFailure(
errorCode: createEmailAccountException.errorCode,
);
} catch (e) {
return const SignUpResultFailure();
}
}
}
And here is the test:
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pocket_open_access/app/features/authentication/data/authentication_data_provider.dart';
import 'package:pocket_open_access/app/features/authentication/repository/authentication_repository.dart';
class MockAuthenticationDataProvider extends Mock
implements AuthenticationDataProvider {}
void main() {
late AuthenticationRepository authenticationRepository;
late AuthenticationDataProvider mockAuthenticationDataProvider;
const String email = 'test#test.com';
const String password = '1234567';
setUp(
() {
mockAuthenticationDataProvider = MockAuthenticationDataProvider();
authenticationRepository = AuthenticationRepository(
authenticationDataProvider: mockAuthenticationDataProvider,
);
},
);
group(
'Sign-up user:',
() {
test(
'is successful so returns SignUpResultSuccess()',
() {
when(() => mockAuthenticationDataProvider.createEmailAccount(
email: any(named: 'email'),
password: any(named: 'password'),
)).thenAnswer(
(_) async {},
);
expect(
authenticationRepository.signUp(email: email, password: password),
Future.value(const SignUpResultSuccess()));
},
);
test(
'fails so returns SignUpResultFailure with error code',
() {
when(
() => mockAuthenticationDataProvider.createEmailAccount(
email: any(named: 'email'),
password: any(named: 'password'),
),
).thenThrow(CreateEmailAccountException(errorCode: 'error-code'));
expect(
authenticationRepository.signUp(email: email, password: password),
Future.value(const SignUpResultFailure(errorCode: 'error-code')));
},
);
},
);
}

Thanks #jamesdlin. I used the following which now works :-)
test(
'is successful so returns SignUpResultSuccess()',
() async {
when(() => mockAuthenticationDataProvider.createEmailAccount(
email: any(named: 'email'),
password: any(named: 'password'),
)).thenAnswer(
(_) async {},
);
const SignUpResultSuccess expected = SignUpResultSuccess();
final SignUpResult actual = await authenticationRepository.signUp(
email: email, password: password);
expect(actual, expected);
},
);

Related

FakeUsedError: 'execute' No Stub was found

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;
});
});
}

Bad state: No method stub was called from within `when()`. Was a real method called, or perhaps an extension method?

I would like to test my Signin Cubit, which depends on AuthenticationRepository and DeviceRepository.
The AuthenticationRepository is used to make the API call, with the ApiResponse class helping.
The ApiResponse class has a functionfromHttp. This is used to decode the Dio Response from json and with a successful response I get a TokenModel and with an unsuccessful response TokenErrors.
The DeviceRepository queries the device data and sends this with the login request.
Now I get the error "Bad state: No method stub was called from within `when()`. Was a real method called, or perhaps an extension method?"
This error only occurs when using the when function with .thenAnswer((_) => Future.value(tResponseSuccess));. Because no real request is possible, I created tResponseSuccess as a model consisting the ApiResponse and the tokenmodel and tokenerrors.
dynamic tDeviceDetails = DeviceModel(
name: 'unit_test_device_name',
systemName: 'unit_test_system_name',
systemVersion: 'unit_test_system_version',
model: 'unit_test_device_model',
localizedModel: 'unit_test_device_localizely_model',
identifierForVendor: 'unit_test_device_identifier_for_vendor',
isPhysicalDevice: false,
appVersionNumber: 'unit_test_app_version_number',
appBuildNumer: 'unit_test_app_build_number',
os: 'unit_test_device_os',
);
const String tDeviceIP = 'unit_test_device_ip';
const String tToken = 'unit_test_long';
const String tFbToken = 'unit_test_long';
const int tUserID = 12345;
ApiResponse<TokenModel, TokenErrors> tResponseSuccess = ApiResponse(
(data) => TokenModel.fromMap({
'access_token': tToken,
'fb_token': tFbToken,
'userId': tUserID,
'success': true,
'message': tFbToken,
}),
(error) => TokenErrors.fromMap({
'access_token': tToken,
'fb_token': tFbToken,
'userId': tUserID,
'success': true,
'message': tFbToken,
}),
);
class MockAuthenticationRepository extends Mock
implements AuthenticationRepository {
#override
Future<ApiResponse<TokenModel, TokenErrors>> login({
required String email,
required String pin,
required String deviceID,
String? deviceOS,
String? deviceModel,
dynamic deviceDetails,
String? deviceIP,
String? lat,
String? long,
}) {
return Future.value(tResponseSuccess);
}
}
class MockDeviceRepository extends Mock implements DeviceRepository {
#override
Future<DeviceModel?> getDevice() async {
return Future.delayed(const Duration(seconds: 2), () => tDeviceDetails);
}
#override
Future<String?> getIP() async {
return Future.delayed(const Duration(seconds: 2), () => tDeviceIP);
}
#override
Future<Position?> getCurrentPosition() async {
return Future.delayed(
const Duration(seconds: 2),
() => Position(
longitude: 12.02,
latitude: 13.02,
timestamp: DateTime.now(),
accuracy: 1.0,
altitude: 1.0,
heading: 1.0,
speed: 1.0,
speedAccuracy: 1.0,
),
);
}
}
void main() {
WidgetsFlutterBinding.ensureInitialized();
late MockAuthenticationRepository mockAuthenticationRepository;
late MockDeviceRepository mockDeviceRepository;
late SigninCubit signinCubit;
setUp(() {
mockAuthenticationRepository = MockAuthenticationRepository();
mockDeviceRepository = MockDeviceRepository();
signinCubit = SigninCubit(
deviceRepository: mockDeviceRepository,
authenticationRepository: mockAuthenticationRepository,
);
});
group('login()', () {
const String tEmail = 'unit_test#test.de';
const String tPin = '12345789';
const String tDeviceID = 'unit_test_device_id';
const String tDeviceOS = 'unit_test_os';
const String tDeviceModel = 'unit_test_device_model';
const String tLat = 'unit_test_lat';
const String tLong = 'unit_test_long';
blocTest<SigninCubit, SigninState>(
'emits [AuthenticationStatusEnum.initial, AuthenticationStatusEnum.loading, AuthenticationStatusEnum.successfullSavedToken] when saveToken() is called and true.',
build: () => signinCubit,
setUp: () {
signinCubit.emit(
signinCubit.state.copyWith(email: tEmail, pin: tPin),
);
when(
() => mockAuthenticationRepository.login(
deviceID: tDeviceID,
email: tEmail,
pin: tPin,
deviceOS: tDeviceOS,
deviceModel: tDeviceModel,
deviceDetails: tDeviceDetails,
deviceIP: tDeviceIP,
lat: tLat,
long: tLong,
),
).thenAnswer((_) => Future.value(tResponseSuccess));
},
act: (cubit) => cubit.login(),
expect: () => const <SigninState>[
SigninState(status: SigninStatusEnum.initial),
SigninState(status: SigninStatusEnum.loading),
SigninState(
status: SigninStatusEnum.successfullLoggedIn,
),
],
);
});
}

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.

How to setup blocTest for a feature that requires authentication?

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.

Testing bloc events type 'Null' is not a subtype of type

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))
]);