mocktail
mocktail copied to clipboard
Flutter Secure Storage Mock Throws type 'Null' is not a subtype of type 'Future<String?>'
Hello, could someone suggest me an idea how to solve this? When im trying to mock flutter secure storage i get this error " type 'Null' is not a subtype of type 'Future<String?> "
-This is my storage
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
abstract class IStorage {
Future
Future<String> getId(String key); }
class MyStorage implements IStorage { MyStorage(this._storage); final FlutterSecureStorage _storage; @override Future<String> getId(String key) async { return await _storage.read(key: key) ?? 'No data founded'; }
@override
Future
-My Service
import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:fixed/shared/storage.dart'; import 'package:flutter/cupertino.dart';
class ApiService { final String _baseURL = 'https://jsonplaceholder.typicode.com/'; Dio _dio = Dio(BaseOptions()); final IStorage _storage;
ApiService(this._storage, {Dio? dio}) { _dio = dio ?? Dio(); _dio.options.baseUrl = _baseURL; }
Future<String> get() async { String id = await _storage.getId('id'); final response = await _dio.get('${_baseURL}posts/$id'); debugPrint(json.encode(response.data)); return json.encode(response.data); } }
My Test File
import 'package:dio/dio.dart'; import 'package:fixed/api/api_service.dart'; import 'package:fixed/shared/storage.dart'; import 'package:fixed/store/store1.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http_mock_adapter/http_mock_adapter.dart'; import 'package:mocktail/mocktail.dart';
class MockStorage extends Mock implements IStorage {}
class MockStorage2 extends Mock implements MyStorage {}
class MockFlutterSecureStorage extends Mock implements FlutterSecureStorage {}
void main() { final dio = Dio(); final dioAdapter = DioAdapter(dio: dio, matcher: const UrlRequestMatcher()); late ApiService service; late MockFlutterSecureStorage mockSecureStorage;
setUp(() { mockStorage = MockStorage(); mockSecureStorage = MockFlutterSecureStorage(); dio.httpClientAdapter = dioAdapter; service = ApiService(dio: dio, MyStorage(mockSecureStorage)); });
void mockData() { dioAdapter.onGet('https://jsonplaceholder.typicode.com/posts/1', (request) { return request.reply(200, {'data': 'sucessfull'}); }); }
void mockGetId() { when(() => mockStorage.getId('id')) .thenAnswer((invocation) => Future.value('1')); }
group('Test Service', () { test('Test endpoint', () async { //IT THROW ERROR when(() => mockSecureStorage.read(key: 'íd')).thenAnswer((_) async => ''); final data = await service.get(); expect(data, equals('{"data":"sucessfull"}')); verify(() => mockSecureStorage.read(key: 'id')).called(1); });
}); }
Hey @DiegoVega19, please format your code first and insert the whole error if you can. 🙏 https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
i know it's been a year but i have the same problem here and for the sake of brevity i'll show only the write function.. and the weird thing is that when i test the saveLocalData isolating the storage.write as you'll see below the test passes but inside my authenticateCrewMember it does not.. my AuthProvider uses the StorageService as a dependency and DI is made with get_it on app load
abstract class StorageService {
Future<bool> write({
required String key,
required String value,
bool deleteAll = false,
});
}
class StorageServiceImpl implements StorageService {
StorageServiceImpl(this._storage);
final FlutterSecureStorage? _storage;
@override
Future<bool> write({
required String key,
required String value,
bool deleteAll = false,
}) async {
try {
await _storage?.write(
key: key,
value: value,
aOptions: getStorageAndroidOptions(),
);
return true;
} on PlatformException catch (e, s) {
log('[PLATFORM-EXCEPTION] - STORAGE-SERVICE:WRITE: $e, $s');
if (deleteAll) {
await _storage?.deleteAll();
}
return false;
} catch (e, s) {
log('[ERROR] - STORAGE-SERVICE:WRITE: $e, $s');
return false;
}
}
}
Auth Provider
class AuthProvider with ChangeNotifier {
AuthProvider({
required this.storageService,
required this.onboardService,
});
final StorageService storageService;
final OnboardService onboardService;
CrewModel? _crew;
CrewModel? get crew => _crew;
bool get isAuth {
return _crew != null && _crew!.qrcodePayload.isNotEmpty;
}
Future<bool> authenticateCrewMember({required String qrCodePayload}) async {
log('AUTHENTICATING CREW MEMBER...');
try {
CrewModel? crew = await onboardService.authenticateCrewMember(
qrCodePayload: qrCodePayload,
);
if (crew == null) {
throw AppException('O QRCode apresentado não retornou dados.');
}
_crew = crew.copyWith(qrcodePayload: qrCodePayload);
await saveLocalData(_crew!);
log('[RESPONSE:PRD] - AUTH-CREW-MEMBER: _crew: $_crew');
notifyListeners();
return true;
} catch (e, s) {
log('[ERROR:PRD] - AUTH-CREW-MEMBER - $e, $s');
throw AppException('$e');
}
}
Future<void> saveLocalData(CrewModel crew) async {
log('saving data');
try {
await storageService.write(
key: STORAGE_CREW_AUTH_KEY,
value: crew.toJson(),
);
log('data saved');
} catch (e, s) {
log('save error: $e, $s');
}
}
}
AuthProviderTest
class MockStorageService extends Mock implements StorageService {}
class MockOnboardService extends Mock implements OnboardService {}
void main() {
late StorageService storageService;
late OnboardService onboardService;
late AuthProvider authProvider;
setUp(() {
storageService = MockStorageService();
onboardService = MockOnboardService();
authProvider = AuthProvider(
storageService: storageService,
onboardService: onboardService,
);
});
tearDown(() {
resetMocktailState();
});
group('AuthProvider.saveLocalData -', () {
*** this test passes ***
test('should call saveLocalData and return void when successful', () async {
final tModel = CrewModel.empty();
when(() => storageService.write(
key: STORAGE_CREW_AUTH_KEY,
value: tModel.toJson(),
)).thenAnswer((_) => Future.value(true));
await authProvider.saveLocalData(tModel);
verify(() => storageService.write(
key: STORAGE_CREW_AUTH_KEY,
value: tModel.toJson(),
)).called(1);
});
});
group('AuthProvider.authenticateCrewMember -', () {
test(
'should call authenticateCrewMember and return a CrewModel when successful',
() async {
const qrCodePayload = 'xxx';
final tModel = CrewModel.empty();
when(() => onboardService.authenticateCrewMember(
qrCodePayload: qrCodePayload))
.thenAnswer((_) => Future.value(tModel));
*** this throws the error ***
when(() => storageService.write(
key: STORAGE_CREW_AUTH_KEY,
value: tModel.toJson(),
)).thenAnswer((_) => Future.value(true));
final result = await authProvider.authenticateCrewMember(
qrCodePayload: qrCodePayload);
expect(result, true);
expect(authProvider.crew, isNotNull);
expect(authProvider.crew?.qrcodePayload, qrCodePayload);
verify(() => onboardService.authenticateCrewMember(
qrCodePayload: qrCodePayload)).called(1);
verifyNoMoreInteractions(onboardService);
*** this test does not pass because it is not called ***
verify(() => storageService.write(
key: STORAGE_CREW_AUTH_KEY,
value: tModel.toJson(),
)).called(1);
verifyNoMoreInteractions(storageService);
});
});
}
Thats the error when I run debug in the test
type 'Null' is not a subtype of type 'Future<bool>', #0 MockStorageService.write (file:///Users/XXX/Dev/YYY/my-app/test/provider/auth.provider_test.dart:11:7)
#1 AuthProvider.saveLocalData (package:my-app/provider/auth.provider.dart:148:28)
#2 AuthProvider.authenticateCrewMember (package:my-app/provider/auth.provider.dart:60:13)
<asynchronous suspension>
#3 main.<anonymous closure>.<anonymous closure> (file:///Users/XXX/Dev/YYY/my-app/test/provider/auth.provider_test.dart:109:22)
<asynchronous suspension>
#4 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:215:9)
<asynchronous suspension>
#5 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:213:7)
<asynchronous suspension>
#6 Invoker._waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:258:9)
<asynchronous suspension>
any help will be very much appreciated.. cheers
forgot:
Flutter doctor
[✓] Flutter (Channel stable, 3.16.0, on macOS 13.2.1 22D68 darwin-arm64, locale en-BR)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.1)
[✓] VS Code (version 1.87.2)
[✓] Connected device (3 available)
[✓] Network resources
flutter_secure_storage: ^9.0.0
mocktail: ^1.0.3
If anyone is still having this issue can you please provide a link to a minimal reproduction sample? it's much easier for me to help if I'm able to reproduce the issue locally, thanks!