Throw Exception in Flutter Test - flutter

I am following this tutorial for Clean Architecture in flutter
This is my test file
test(
'should return server failure when call to remote data is unsuccessful',
() async {
// arrange
when(mockRemoteDataSource.getConcreteNumberTrivia(any))
.thenThrow(ServerException());
// act
final result = await repository.getConcreteNumberTrivia(tNumber);
// assert
verify(mockRemoteDataSource.getConcreteNumberTrivia(tNumber));
verifyZeroInteractions(mockLocalDataSource); // <--- this line is failing
expect(result, equals(Left(ServerFailure()))); // // <--- this line is failing
},
);
This is my code
Future<Either<Failure, NumberTrivia>> getConcreteNumberTrivia(int number) async {
networkInfo.isConnected;
try {
final remoteTrivia = await remoteDataSource.getConcreteNumberTrivia(number);
localDataSource.cacheNumberTrivia(remoteTrivia);
return Right(remoteTrivia);
} on ServerException {
return Left(ServerFailure());
}
}
I dont know why but coz of these 2 lines, test case is failing.
verifyZeroInteractions(mockLocalDataSource);
expect(result, equals(Left(ServerFailure())));
I have mentioned in test case to throw a server exception using when and thenThrow but still it goes to this line localDataSource.cacheNumberTrivia(remoteTrivia);. I think this is the cause of the error but I am not quite sure.
I did read the docs but I could not find why is this problem occuring. What am i doing wrong? I am new to TDD in flutter. Thanks in advance :)

verifyZeroInteractions(mockLocalDataSource);
Fail is normal because you called it in you getConcreteNumberTrivia function (localDataSource.cacheNumberTrivia) and you are expecting in your test no interaction. Just remove it, it should pass.
expect(result, equals(Left(ServerFailure())));
For the this one, can you try this ?
result.fold((left) {
expect(left, isA<ServerFailure>());
}, (r) => fail("test failed"));

Related

Flutter Future timeouts not always working correctly

Hey I need some help here for How to use timeouts in flutter correctly. First of all to explain what the main goal is:
I want to recive data from my Firebase RealTime Database but need to secure this request api call with an time out of 15 sec. So after 15 sec my timeout should throw an exception that will return to the Users frontend the alert for reasons of time out.
So I used the simple way to call timeouts on future functions:
This functions should only check if on some firebase node an ID is existing or not:
Inside this class where I have declared this functions I also have an instance which called : timeoutControl this is a class which contains a duration and some reasons for the exceptions.
Future<bool> isUserCheckedIn(String oid, String maybeCheckedInUserIdentifier, String onGateId) async {
try {
databaseReference = _firebaseDatabase.ref("Boarding").child(oid).child(onGateId);
final snapshot = await databaseReference.get().timeout(Duration(seconds: timeoutControl.durationForTimeOutInSec), onTimeout: () => timeoutControl.onEppTimeoutForTask());
if(snapshot.hasChild(maybeCheckedInUserIdentifier)) {
return true;
}
else {
return false;
}
}
catch (exception) {
return false;
}
}
The TimeOutClass where the instance timeoutControl comes from:
class CustomTimeouts {
int durationForTimeOutInSec = 15; // The seconds for how long to try until we throw an timeout exception
CustomTimeouts();
// TODO: Implement the exception reasons here later ...
onEppTimeoutForUpload() {
throw Exception("Some reason ...");
}
onEppTimeoutForTask() {
throw Exception("Some reason ...");
}
onEppTimeoutForDownload() {
throw Exception("Some reason ...");
}
}
So as you can see for example I tried to use this implementation above. This works fine ... sometimes I need to fight with un explain able things -_-. Let me try to introduce what in somecases are the problem:
Inside the frontend class make this call:
bool isUserCheckedIn = await service.isUserCheckedIn(placeIdentifier, userId, gateId);
Map<String, dynamic> data = {"gateIdActive" : isUserCheckedIn};
/*
The response here is an Custom transaction handler which contains an error or an returned param
etc. so this isn't relevant for you ...
*/
_gateService.updateGate(placeIdentifier, gateId, data).then((response) {
if(response.hasError()) {
setState(() {
EppDialog.showErrorToast(response.getErrorMessage()); // Shows an error message
isSendButtonDiabled = false; /*Reset buttons state*/
});
}
else {
// Create an gate process here ...
createGateEntrys(); // <-- If the closures update was successful we also handle some
// other data inside the RTDB for other reasons here ...
}
});
IMPORTANT to know for you guys is that I am gonna use the returned "boolean" value from this function call to update some other data which will be pushed and uploaded into another RTDB other node location for other reasons. And if this was also successful the application is going on to update some entrys also inside the RTDB -->createGateEntrys()<-- This function is called as the last one and is also marked as an async function and called with its closures context and no await statement.
The Data inside my Firebase RTDB:
"GateCheckIns" / "4mrithabdaofgnL39238nH" (The place identifier) / "NFdxcfadaies45a" (The Gate Identifier)/ "nHz2mhagadzadzgadHjoeua334" : 1 (as top of the key some users id who is checked in)
So on real devices this works always without any problems... But the case of an real device or simulator could not be the reason why I'am faceing with this problem now. Sometimes inside the Simulator this Function returns always false no matter if the currentUsers Identifier is inside the this child nodes or not. Therefore I realized the timeout is always called immediately so right after 1-2 sec because the exception was always one of these I was calling from my CustomTimeouts class and the function which throws the exception inside the .timeout(duration, onTimeout: () => ...) call. I couldn't figure it out because as I said on real devices I was not faceing with this problem.
Hope I was able to explain the problem it's a little bit complicated I know but for me is important that someone could explain me for what should I pay attention to if I am useing timeouts in this style etc.
( This is my first question here on StackOverFlow :) )

Invalid argument event trying unit test flutter with Mockito

Event i trying to flutter test, i get issue's invalid argument, in other case i explain same method to testing and result running as well, but i don't what a mockito mean at the issue's cause in other case i success with same method, let see my fetch test with mockito :
testing
test('should return list of tv/on_the_airs when response code is 200 ',
() async {
// arrange
when(mockHttpClient.get(Uri.parse('$BASE_URL/tv/top_rated?$API_KEY')))
.thenAnswer((_) async => http.Response(
'{"page": 1,"results": [ {"backdrop_path": "/4sSzTvk200BQyYjRJq69mLwE9xG.jpg","first_air_date": "2018-04-25","genre_ids": [16,35,18,10759],"id": 79141,"name": "Scissor Seven","origin_country": ["CN"],"original_language": "zh","original_name": "刺客伍六七","overview": "Seeking to recover his memory, a scissor-wielding, hairdressing, bungling quasi-assassin stumbles into a struggle for power among feuding factions.","popularity": 76.07,"poster_path": "/A39DWUIrf9WDRHCg7QTR8seWUvi.jpg","vote_average": 8.8,"vote_count": 457},],"total_pages": 125,"total_results": 2496}',
200));
// act
final result = await dataSource.getTopRatedTvSeriess();
// assert
expect(result, tTvSeriesList);
});
then i have error:
Invalid argument (string): Contains invalid characters.: "{\"page\": 1,\"results\": [ {\"backdrop_path\": \"/4sSzTvk200BQyYjRJq69mLwE9xG.jpg\",\"first_air_date\": \"2018-04-25\",\"genre_ids\": [16,35,18,10759],\"id\": 79141,\"name\": \"Scissor Seven\",\"origin_country\": [\"CN\"],\"original_language\": \"zh\",\"original_name\": \"刺客伍六七\",\"overview\": \"Seeking to recover his memory, a scissor-wielding, hairdressing, bungling quasi-assassin stumbles into a struggle for power among feuding factions.\",\"popularity\": 76.07,\"poster_path\": \"/A39DWUIrf9WDRHCg7QTR8seWUvi.jpg\",\"vote_average\": 8.8,\"vote_count\": 457},],\"total_pages\": 125,\"total_results\": 2496}"

Flutter - Expected: should do the following in order: emits an event Empty, NumberTriviaError, Actual: Empty: Which was not a Stream or a SteamQue

I'm doing Flutter Clean Architecture TDD course from closely 20days. I'm writing unit test of Stream (mapEventToState) here is my Test code :
test(
'should emit [Error] when the input is invalid',
() async {
// arrange
when(mockInputConverter!.stringToUnsignedInteger(SIGNED_STRINGS))
.thenReturn(Left(InvalidInputFailure()));
// assert later
final expected = [
// The initial state is always emitted first
NumberTriviaEmpty(),
NumberTriviaError(message: INVALID_INPUT_FAILURE_MESSAGE),
];
expectLater(bloc!.state, emitsInOrder(expected));
// act
bloc!.add(GetTriviaForConcreteNumber(tNumberString));
},
);
Just forget all about (when), because It's fine I've mocked all classes and simply return a Left of Either which is obviously a Failure. So the problem rise in expected data, I just created list of expected and passed it inside the method the "expectLater" in order "emitsInOrder".
My Implementation code of Stream is is here :
#override
Stream<NumberTriviaState> mapEventToState(NumberTriviaEvent event,) async* {
if (event is GetTriviaForConcreteNumber) {
final inputEither =
inputConverter.stringToUnsignedInteger(event.numberString);
yield* inputEither!.fold(
(failure) async* {
yield NumberTriviaError(message: INVALID_INPUT_FAILURE_MESSAGE);
},
// Although the "success case" doesn't interest us with the current test,
// we still have to handle it somehow.
(integer) => throw UnimplementedError(),
);
}
The [ Error ] I have put in the Title isn't completed because It's a little bit long I'm sharing it here please take a look upon It what actually I'm doing wrong.
Expected: should do the following in order:
• emit an event that NumberTriviaEmpty:<NumberTriviaEmpty()>
• emit an event that NumberTriviaError:<NumberTriviaError(Invalid Input - The number must be a positive integer or zero.)>
Actual: NumberTriviaEmpty:<NumberTriviaEmpty()>
Which: was not a Stream or a StreamQueue
I have done all these the same, what it expect from me but still it's throwing this exception. I stuck in this error about 4 days I search everywhere but I didn't find any fix, Kindly If I'm doing something wrong just throw your recommendation in the comment section or Answer. Thank you :)
In addition to changing 'bloc!.state' to 'bloc' (per QuangNV and ouflak), try also changing the 'async' in your test to the asynchronous generator, 'async*', to match to the code used in your Stream implementation.
test(
'should emit [Error] when the input is invalid',
() async* {
// arrange
I used another way by using the bloc_test: ^8.0.0 package and the flutter_bloc: ^7.0.0
blocTest<NumberTriviaBloc, NumberTriviaState>(
'should emits [LoadingState, ErrorState(InputConvert failure..)] when add event [getConcreteNumberTrivia] with invaildString ',
build: () => numberTriviaBloc,
setUp: () => when(mockInputConverter.stringToUnsignedInteger(any))
.thenReturn(const Left(InvalidInputFailure())),
act: (bloc) =>
bloc.add(const GetConcreteNumberEvent(number: tNumberString)),
expect: () => [
LoadingState(),
ErrorState(message: const InvalidInputFailure().message)
],
);
So the complete answer is
You can try changing from:
expectLater(bloc!.state, emitsInOrder(expected));
To:
expectLater(bloc, emitsInOrder(expected));
In addition to changing 'bloc!.state' to 'bloc' (per QuangNV and ouflak), try also changing the 'async' in your test to the asynchronous generator, 'async*', to match to the code used in your Stream implementation.
test(
'should emit [Error] when the input is invalid',
() async* {
// arrange
You can try changing from:
expectLater(bloc!.state, emitsInOrder(expected));
To:
expectLater(bloc, emitsInOrder(expected));
Hope you can solve it!
I'm also following the same course & in addition of the ) async* { suggestion, I also needed to add quotation marks to have
yield Error(message: "INVALID_INPUT_FAILURE_MESSAGE");
In contrast,
expectLater(bloc.state,
expectLater(bloc!.state,
expectLater(bloc,
had not impact on passing the test successfully.

Flutter : About error handling of timeout when retrieving documents of Firestore

I'm making an app with Flutter (Dart) and Firebase.
displayName = (await instance.collection('users').doc(ds.id).get())
.data()['displayName'];
When retrieving the Firestore document as described above, for example,
if I cannot get it after waiting 10 seconds,
I want to set the displayName to '' (empty string).
How should I write if I want to do something like that?
try {
Future.delayed(Duration(seconds:5,),(){
throw Exception('eeeeeewwwwwwwww');
});
await Future.delayed(Duration(seconds:10),);
//When I actually execute the code of ↓,
//there are almost no cases where it takes time to acquire (it can be acquired in a short time),
//so I tried to write a pseudo case like ↑ that can not be acquired.
/*
displayName = (await instance.collection('users').doc(ds.id).get())
.data()['displayName'];
url = (await instance.collection('users').doc(ds.id).get())
.data()['photoUrl'];
*/
}catch(e){
displayName='time out';
url=null;
continue;//(← Since it is in a for statement, if it takes 10 seconds or more, I want to skip to the next loop.)
}
The above code came to my mind, and I thought I could do it, but when I actually executed it,
The exception is thrown, but the catch clause is not executed (the exception was not caught).
I'm not sure why this doesn't work, but why not?
Is there a theory method for such cases?
When I googled, it says that when we use Dio, we can handle such timeout error by just setting the number of seconds (because it is in English, we can only understand the rough atmosphere).
However, the reason why people say "Dio looks good" is that if we don't use Dio, we have to write this kind of processing ourself, but with Dio we can easily do it?
You can try something like this although I haven't tested the code:
Future<String> fetchOrSetUser() async {
final String displayName;
try {
final userRef = await instance.collection('users').doc(ds.id).get().timeout(Duration(seconds: 10));
displayName = userRef.data()['displayName'];
} on TimeoutException catch (error) {
await instance.collection('users').doc(ds.id).update({
'displayName': ''
});
displayName = '';
} catch (error, _) {
rethrow;
}
return displayName;
}
It sets a timeout on the async call of 10 seconds and then tries to catch the TimeoutException. If that happens, it updates the userRef's displayName in the on TimeoutException block. I'm not sure if this would also fail if the user has no internet but I think updates can be written without internet... ?
Let me know if it doesn't work

await doesnt work with stream for me in flutter

I have a problem where i want to read some data from database and i want my function to wait for the data before proceeding executing the rest of my code. i am using stream with await and async but doesnt look like it is working for me.
here is my code
void updateIncome() async {
Stream<List<IncomeData>> _currentEntries;
_currentEntries = database.watchIncomeForUpdate(this.income);
await _currentEntries.forEach((List<IncomeData> x) {
x.forEach((element) {
print('AWAIT');
}
);
});
print('FINISH');
}
here is the procedure that call my database and get data
Stream<List<IncomeData>> watchIncomeForUpdate(IncomeData entry) {
return (select(income)..where((t) =>t.id.isBiggerOrEqualValue(entry.id) & t.groupId.equals(entry.groupId))
..orderBy([(t) => OrderingTerm(expression: t.dateReceived)])).watch();
}
when i run the function updateIncome(), it prints FINISH first which make me believe that the await/async is not working by waiting for the foreach to loop through all elements in the list.
i tried to move the await keyword in the function call
_currentEntries = await database.watchIncomeForUpdate(this.income);
i get a warning message: await applied to Stream<List> which i not a Future
can someone help me? what i am doing wrong?
i want to wait for database to get the data, loop and print AWAIT then when finish, it should proceed with rest of code and print FINISH. the function that call the database return 8 rows. so when i loop using foreach, it should print AWAIT 8 times follow by FINISH.
how can i fix my code so that the function calls the database ,loop through the elements and wait until the loop finish before proceeding with the rest of the code outside of the loop?
Since watchIncomeForUpdate is not a Future function, you can't wait for a non-future function.
void updateIncome() async {
await for(var x in database.watchIncomeForUpdate(this.income)){
x.forEach((element) {
print('AWAIT');
}
);
});
print('FINISH');
}
Ref: https://dart.dev/tutorials/language/streams
Thanks for all replies. i figured it out.
i changed function from this
Stream<List<IncomeData>> watchIncomeForUpdate(IncomeData entry) {
return (select(income)..where((t) =>t.id.isBiggerOrEqualValue(entry.id) & t.groupId.equals(entry.groupId))
..orderBy([(t) => OrderingTerm(expression: t.dateReceived)])).watch();
}
to this
Future<List<IncomeData>> watchIncomeForUpdate(IncomeData entry) async {
return (select(income)..where((t) =>t.id.isBiggerOrEqualValue(entry.id) & t.groupId.equals(entry.groupId))
..orderBy([(t) => OrderingTerm(expression: t.dateReceived)])).get();
}
then call the procedure as
data = await database.watchIncomeForUpdate(this.income);