I'm trying to test the BLoCs in my Flutter App but I hit a wall with this problem below.
===== asynchronous gap ===========================
dart:async _AsyncAwaitCompleter.completeError
package:bloc_test/src/bloc_test.dart runBlocTest.<fn>
dart:async runZoned
package:bloc_test/src/bloc_test.dart 157:9 runBlocTest
package:bloc_test/src/bloc_test.dart 127:11 blocTest.<fn>
Expected: [
ChangePasswordLoading:ChangePasswordLoading,
ChangePasswordFailure:ChangePasswordFailure
]
Actual: [
ChangePasswordLoading:ChangePasswordLoading,
ChangePasswordSuccess:ChangePasswordSuccess
]
Which: at location [1] is ChangePasswordSuccess:<ChangePasswordSuccess> instead of ChangePasswordFailure:<ChangePasswordFailure>
package:test_api expect
package:bloc_test/src/bloc_test.dart 176:9 runBlocTest.<fn>
===== asynchronous gap ===========================
dart:async _asyncThenWrapperHelper
package:bloc_test/src/bloc_test.dart runBlocTest.<fn>
dart:async runZoned
package:bloc_test/src/bloc_test.dart 157:9 runBlocTest
package:bloc_test/src/bloc_test.dart 127:11 blocTest.<fn>
which is caused by this Failing BLoC test
blocTest<ChangePasswordBloc, ChangePasswordState>(
'emits [ChangePasswordLoading, ChangePasswordFailure] on failed ChangePassword',
build: () {
when(authenticationRepository.changePassword(
'token',
'oldPassword',
'newPassword',
'newPasswordConfirm',
)).thenThrow(WebException(403));
return changePasswordBloc;
},
act: (bloc) => bloc
..add(ChangePassword(
oldPassword: 'oldPassword',
newPassword: 'newPassword',
newPasswordConfirm: 'newPasswordConfirm',
)),
expect: [
ChangePasswordLoading(),
ChangePasswordFailure(error: 'Old password is not correct'),
],
errors: [isA<WebException>()],
);
This is the code I have used to test my ChangePasswordBloc (Notice all the other tests are passing successfully)
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_app/business_logic/blocs/change_password/change_password_bloc.dart';
import 'package:flutter_app/business_logic/blocs/change_password/change_password_event.dart';
import 'package:flutter_app/business_logic/blocs/change_password/change_password_state.dart';
import 'package:flutter_app/data/exceptions/web_exception.dart';
import 'package:flutter_app/data/repositories/authentication_repository.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
class MockAuthenticationRepository extends Mock
implements AuthenticationRepository {}
class MockSecureStorage extends Mock implements FlutterSecureStorage {}
main() {
ChangePasswordBloc changePasswordBloc;
MockSecureStorage secureStorage;
MockAuthenticationRepository authenticationRepository;
setUp(() {
secureStorage = MockSecureStorage();
authenticationRepository = MockAuthenticationRepository();
changePasswordBloc = ChangePasswordBloc(
authenticationRepository,
secureStorage,
);
});
tearDown(() {
changePasswordBloc?.close();
});
test(
'initial state is ChangePasswordInitial',
() => expect(changePasswordBloc.state, ChangePasswordInitial()),
);
group('ChangePassword process', () {
blocTest<ChangePasswordBloc, ChangePasswordState>(
'emits [ChangePasswordLoading, ChangePasswordSuccess] on successful ChangePassword',
build: () {
when(authenticationRepository.changePassword(
'token',
'oldPassword',
'newPassword',
'newPasswordConfirm',
)).thenAnswer((_) async => null);
return changePasswordBloc;
},
act: (bloc) => bloc
..add(ChangePassword(
oldPassword: 'oldPassword',
newPassword: 'newPassword',
newPasswordConfirm: 'newPasswordConfirm',
)),
expect: [
ChangePasswordLoading(),
ChangePasswordSuccess(),
],
);
blocTest<ChangePasswordBloc, ChangePasswordState>(
'emits [ChangePasswordLoading, ChangePasswordFailure] on failed ChangePassword',
build: () {
when(authenticationRepository.changePassword(
'token',
'oldPassword',
'newPassword',
'newPasswordConfirm',
)).thenThrow(WebException(403));
return changePasswordBloc;
},
act: (bloc) => bloc
..add(ChangePassword(
oldPassword: 'oldPassword',
newPassword: 'newPassword',
newPasswordConfirm: 'newPasswordConfirm',
)),
expect: [
ChangePasswordLoading(),
ChangePasswordFailure(error: 'Old password is not correct'),
],
errors: [isA<WebException>()],
);
});
}
This is my ChangePasswordBloc code
ChangePasswordBloc
import 'dart:async';
import 'package:flutter_app/data/exceptions/web_exception.dart';
import 'package:flutter_app/data/repositories/authentication_repository.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:bloc/bloc.dart';
import 'change_password_event.dart';
import 'change_password_state.dart';
class ChangePasswordBloc
extends Bloc<ChangePasswordEvent, ChangePasswordState> {
final AuthenticationRepository _authenticationRepository;
final FlutterSecureStorage _secureStorage;
ChangePasswordBloc(AuthenticationRepository authenticationRepository,
FlutterSecureStorage secureStorage)
: _authenticationRepository = authenticationRepository,
_secureStorage = secureStorage,
super(ChangePasswordInitial());
@override
Stream<ChangePasswordState> mapEventToState(
ChangePasswordEvent event,
) async* {
if (event is ChangePassword) {
yield* _mapChangePasswordToState(event);
}
}
Stream<ChangePasswordState> _mapChangePasswordToState(
ChangePassword event) async* {
yield ChangePasswordLoading();
try {
final accessToken = await _secureStorage.read(key: 'accessToken');
await _authenticationRepository.changePassword(
accessToken,
event.oldPassword,
event.newPassword,
event.newPasswordConfirm,
);
yield ChangePasswordSuccess();
} on WebException catch (e) {
String errorMessage;
if (e.statusCode == 422) {
errorMessage = 'Password must be 8 characters long';
} else if (e.statusCode == 419) {
errorMessage = 'New Password is not matching';
} else if (e.statusCode == 403) {
errorMessage = 'Old password is not correct';
}
yield ChangePasswordFailure(error: errorMessage ?? e.toString());
} catch (err) {
yield ChangePasswordFailure(
error: err.toString() ?? 'An unknown error occurred');
}
}
}
As you can tell, if a WebException is thrown, I yield ChangePasswordFailure() with an error message. This does work on the actual app, so I am certain that logic works, yet the test does not seem to catch that thrown WebException.
ChangePasswordEvent
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
abstract class ChangePasswordEvent extends Equatable {
@override
List<Object> get props => [];
}
class ChangePassword extends ChangePasswordEvent {
final String oldPassword;
final String newPassword;
final String newPasswordConfirm;
ChangePassword({
@required this.oldPassword,
@required this.newPassword,
@required this.newPasswordConfirm,
});
@override
List<Object> get props => [oldPassword, newPassword, newPasswordConfirm];
}
ChangePasswordState
import 'package:meta/meta.dart';
import 'package:equatable/equatable.dart';
abstract class ChangePasswordState extends Equatable {
@override
List<Object> get props => [];
}
class ChangePasswordInitial extends ChangePasswordState {}
class ChangePasswordLoading extends ChangePasswordState {}
class ChangePasswordSuccess extends ChangePasswordState {}
class ChangePasswordFailure extends ChangePasswordState {
final String error;
ChangePasswordFailure({@required this.error});
@override
List<Object> get props => [error];
}
Any suggestions or advice as to why .thenThrow(WebException(403)) is not actually being caught when it actually works on the real Flutter App (if a WebException is thrown, ChangePasswordFailure is always thrown)?
I have another example with the same code which does work (The code for ClientInfoBloc is handles WebExceptions in the same way as ChangePasswordBloc and it also works in the real Flutter app)
Working Test Example with thrown WebException
I checked this related issue but it did not fix anything.
Aucun commentaire:
Enregistrer un commentaire