mocktail
mocktail copied to clipboard
Stub / When a closure / lambda expression
Suppose we have following class (DbContext from Drift Sqlite database)
class LocalDatabaseContext with AppLoggy {
final LocalDatabase _db;
LocalDatabaseContext({LocalDatabase? db}) : _db = db ?? DI.resolve<LocalDatabase>();
Future<Either<T, LocalDatabaseError>> profileSessions<T>(Future<T> Function(ProfileSessionsDao) callback) async {
return _wrapRequest(callback, _db.profileSessionsDao);
}
// this method we want stub / verify
Future<Either<T, LocalDatabaseError>> userProfiles<T>(Future<T> Function(UserProfilesDao) callback) async {
return _wrapRequest(callback, _db.userProfilesDao);
}
Future<Either<T, LocalDatabaseError>> _wrapRequest<T, DAO>(Future<T> Function(DAO) callback, DAO dao) async {
try {
final T result = await callback.call(dao);
return Left(result);
} catch (e) {
loggy.error('Local database call failed with error: ${e.toString()}', e, StackTrace.current);
return Right(UnkownError(Exception(e.toString())));
}
}
}
and a class which uses this Context
Future<Either<UserProfile?, LocalDatabaseError>> getByUserNameAndServer(String userName, ApiServer server) =>
_dbContext.userProfiles((dao) => dao.getByUserNameAndServer(userName: userName, server: server));
This class I want to unit test, it contains more complex methods, this is just an example.
// stub
when(() => dbContext.userProfiles<UserProfile?>(
(dao) => dao.getByUserNameAndServer(userName: any(named: 'userName'), server: any(named: 'server'))))
.thenAnswer((_) async {
return Left(profile);
});
// act
// this method calls internally getByUserNameAndServer
final result = await store.createIfAbsent(userName: 'user1', selectedServer: server);
// verify
// ....
Issue is that as soon as code hits _dbContext.userProfiles((dao) => dao.getByUserNameAndServer(userName: userName, server: server))
it throws type 'Null' is not a subtype of type 'Future<Either<UserProfile?, LocalDatabaseError>>
This means, that mocktail did not matched "closure" call.
One of the workaround is to use any()
when(() => dbContext.userProfiles<UserProfile?>(any())).thenAnswer((_) async {
return Left(profile);
});
but if method under the test uses dbContext multiple times with different "closures" it is not possible to stub it properly and test outcome behavior.
Any ideas how to solve it?
I got the same error! any help, please!
Hi @petrnymsa 👋
Using the any
matcher should work in this case. If this is still an issue please share a link to a complete minimal reproduction sample and I'm happy to take a closer look, thanks!