Hi Folks I am facing an issue regarding unit test in flutter. I am using flutter version 3.10.5 and Mockito plugin. I am not able to identify why this below errors showing. please help me to find out. I have provided necessary files below.
test\unit_test\data_providers\add_patients\add_patient_data_provider_test.dart 14:7 RestClientMock.get test\unit_test\data_providers\add_patients\add_patient_data_provider_test.dart 38:23 main.
type 'Null' is not a subtype of type 'Future'
add_patient_data_provider_test.dart
//@dart=3.0
//ignore_for_file: prefer-match-file-name
import 'package:mockito/mockito.dart';
import 'package:namah/repos/rest_client/rest_client.dart';
import 'package:namah/utilities/constants/api_endpoint.dart';
import 'dart:convert';
import 'package:namah/utilities/constants/api_param_constants.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:retry/retry.dart';
import 'package:namah/data_providers/patients/patient_data_provider.dart';
class RestClientMock extends Mock implements RestClient {}
void main() {
group("add patient api calls", () {
test("fetch patient api call positive call", () async {
/*Given*/
String endPoint = ApiEndPoint.athmaUserPreferenceData;
var statusCode = 200;
String _userPreferenceAthmaUrlCodeValue = "UAA_001";
String responseBody = jsonEncode({
"hospital": {"code": "123"},
});
Map<String, dynamic> queryParams = {
ApiParamConstants.athmaUrlCodeKeyParam:
_userPreferenceAthmaUrlCodeValue,
ApiParamConstants.athmaUrlContentKeyParam:
ApiParamConstants.vitalSaveAthmaUrlContentValue,
};
var mockClient = RestClientMock();
var patientDataProvider = PatientDataProvider(
restClient: mockClient,
);
when(mockClient.get(
endpoint: endPoint,
queryParam: queryParams,
isAthmaTokenRequired: true))
.thenAnswer(
(_) => Future<http.Response>.value(
http.Response(
responseBody,
statusCode,
),
),
);
var result = await patientDataProvider?.getUserPreference();
verify(
mockClient?.get(
endpoint: endPoint,
queryParam: queryParams,
isAthmaTokenRequired: true,
),
).called(1);
expect(result, isA<String>());
});
});
}
rest_client.dart
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:namah/flavor_config.dart';
import 'package:namah/utilities/storage/encrypted_sharedpref_manager.dart';
import 'package:retry/retry.dart';
import '../../utilities/constants/app_constants.dart';
import '../../utilities/helper/network_call_manager.dart';
import '../crash_analytics/app_crashlytics.dart';
class RestClient {
http.Client? _httpClient;
RetryOptions? _retryOption;
AppCrashlytics? _appCrashlytics;
EncryptedSharedPrefManager? _appStorage;
static final RestClient _instance = RestClient._internal();
static const int _REQUEST_TIME_OUT_DURATION_IN_SECONDS = 30;
final String _hcciCountryCode = "1";
RestClient._internal();
factory RestClient({
required http.Client httpClient,
required RetryOptions retryOption,
AppCrashlytics? appCrashlytics,
EncryptedSharedPrefManager? appStorage,
}) {
_instance._httpClient = httpClient;
_instance._retryOption = retryOption;
_instance._appCrashlytics = appCrashlytics;
_instance._appStorage = appStorage;
return _instance;
}
Future<dynamic> get({
required String endpoint,
Map<String, dynamic>? queryParam,
bool isAthmaTokenRequired = false,
}) async {
try {
var baseUrl = await getBaseUrl();
var endPoint = await _getEndPoint(endpoint);
var headers = await _getHeaderConfig(isAthmaTokenRequired);
http.Response? response = await _retryOption?.retry(
() => _httpClient
?.get(
_getUri(
queryParam,
endPoint,
baseUrl,
),
headers: headers,
)
.timeout(Duration(seconds: _REQUEST_TIME_OUT_DURATION_IN_SECONDS)),
retryIf: (e) => e is SocketException || e is TimeoutException,
);
if (response?.statusCode.isSuccessRequest != ResponseHandler.SUCCESSFUL) {
_logApiException(error: response?.body, stackTrace: StackTrace.current);
}
return response;
} on Exception catch (e, stack) {
log("RestClient - Exception : ${e.toString()}");
_logApiException(error: e.toString(), stackTrace: stack);
rethrow;
}
}
Future<dynamic> post({
required String endpoint,
dynamic body,
Map<String, dynamic>? queryParam,
bool isAthmaTokenRequired = false,
bool isFileUploadRequest = false,
}) async {
try {
var baseUrl = await getBaseUrl();
var endPoint = await _getEndPoint(endpoint);
var headers = await _getHeaderConfig(
isAthmaTokenRequired,
isFileUploadRequest: isFileUploadRequest,
);
http.Response? response = await _httpClient
?.post(
_getUri(queryParam, endPoint, baseUrl),
headers: headers,
body: (body != null) ? jsonEncode(body) : '',
)
.timeout(Duration(seconds: _REQUEST_TIME_OUT_DURATION_IN_SECONDS));
if (response?.statusCode.isSuccessRequest != ResponseHandler.SUCCESSFUL) {
_logApiException(error: response?.body, stackTrace: StackTrace.current);
}
return response;
} on Exception catch (e, stack) {
log("RestClient - Exception : ${e.toString()}");
_logApiException(error: e.toString(), stackTrace: stack);
rethrow;
}
}
Future<dynamic> put({
required String endpoint,
dynamic body,
Map<String, String>? queryParam,
bool isAthmaTokenRequired = false,
}) async {
try {
var baseUrl = await getBaseUrl();
var endPoint = await _getEndPoint(endpoint);
var headers = await _getHeaderConfig(isAthmaTokenRequired);
http.Response? response = await _httpClient
?.put(
_getUri(queryParam, endPoint, baseUrl),
headers: headers,
body: (body != null) ? jsonEncode(body) : '',
)
.timeout(Duration(seconds: _REQUEST_TIME_OUT_DURATION_IN_SECONDS));
if (response?.statusCode.isSuccessRequest != ResponseHandler.SUCCESSFUL) {
_logApiException(error: response?.body, stackTrace: StackTrace.current);
}
return response;
} on Exception catch (e, stack) {
log("RestClient - Exception : ${e.toString()}");
_logApiException(error: e.toString(), stackTrace: stack);
rethrow;
}
}
Future<dynamic> delete({
required String endpoint,
Map<String, String>? queryParam,
bool isAthmaTokenRequired = false,
}) async {
try {
var baseUrl = await getBaseUrl();
var endPoint = await _getEndPoint(endpoint);
var headers = await _getHeaderConfig(isAthmaTokenRequired);
http.Response? response = await _retryOption?.retry(
() => _httpClient
?.delete(_getUri(queryParam, endPoint, baseUrl), headers: headers)
.timeout(Duration(seconds: _REQUEST_TIME_OUT_DURATION_IN_SECONDS)),
retryIf: (e) => e is SocketException || e is TimeoutException,
);
if (response?.statusCode.isSuccessRequest != ResponseHandler.SUCCESSFUL) {
_logApiException(error: response?.body, stackTrace: StackTrace.current);
}
return response;
} on Exception catch (e, stack) {
log("RestClient - Exception : ${e.toString()}");
_logApiException(error: e.toString(), stackTrace: stack);
rethrow;
}
}
Future<String> getBaseUrl() async {
var baseUrl = FlavorConfig.flavorValues.apiUrlPrefix;
// if (FlavorConfig.appFlavor == Flavor.namahUat ||
// FlavorConfig.appFlavor == Flavor.namahProd) {
// String country = "0";
// try {
// country =
// await _appStorage?.retrieveEncryptedData(kSelectedCountry) ?? "";
// } catch (e) {
// log(e.toString());
// }
// if (country == _hcciCountryCode) {
// baseUrl = FlavorConfig.appFlavor == Flavor.namahUat
// ? NAMAH_HCCI_UAT_BASEURL
// : NAMAH_HCCI_PROD_BASEURL;
// }
// }
return baseUrl;
}
Future<Map<String, String>> _getHeaderConfig(
bool isAthmaTokenRequired, {
bool isFileUploadRequest = false,
}) async {
Map<String, String> headers = {};
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Connection': 'Keep-Alive',
'Keep-Alive': 'timeout=10, max=1000',
};
String? token = await EncryptedSharedPrefManager.getInstance()
?.retrieveEncryptedData(kIdToken);
if (token != null && token.isNotEmpty) {
headers.putIfAbsent("Authorization", () => 'Bearer $token');
}
if (isAthmaTokenRequired) {
var athma_token = await EncryptedSharedPrefManager.getInstance()
?.retrieveEncryptedData(kAthmaToken);
headers.putIfAbsent(
"athmaToken",
() => athma_token ?? "",
);
}
if (isFileUploadRequest) {
Map<String, String> headers = {};
String? token = await EncryptedSharedPrefManager.getInstance()
?.retrieveEncryptedData(kIdToken);
if (token != null && token.isNotEmpty) {
headers.putIfAbsent("Authorization", () => 'Bearer $token');
headers.putIfAbsent("cache-control", () => 'no-cache');
headers.putIfAbsent("Content-Type", () => 'multipart/form-data');
}
return headers;
}
return headers;
}
Uri _getUri(
Map<String, dynamic>? queryParam,
String endpoint,
String baseUrl,
) {
Uri uri = Uri.https(
baseUrl,
endpoint,
queryParam,
);
log('uri ' + uri.toString());
return uri;
}
void _logApiException({String? error, StackTrace? stackTrace}) {
_appCrashlytics?.recordNonFatalError(
errorMsg: error ?? "",
stack: stackTrace,
);
}
Future<String> _getEndPoint(String endpoint) async {
var sub_url = endpoint;
if (FlavorConfig.appFlavor == Flavor.namahProd) {
String country =
await _appStorage?.retrieveEncryptedData(kSelectedCountry) ?? "";
if (country == _hcciCountryCode) {
sub_url = endpoint.replaceAll("/aadi/", "/");
}
}
return sub_url;
}
}
patient_data_provider.dart
// ignore_for_file: avoid-nested-conditional-expressions
import 'dart:convert';
import 'package:http/http.dart';
import 'package:namah/models/patient_model.dart';
import 'package:namah/models/sync_chat_message_model.dart';
import 'package:namah/models/tasks/task_model.dart';
import 'package:namah/utilities/constants/api_param_constants.dart';
import 'package:namah/utilities/constants/app_task_constants.dart';
import 'package:namah/utilities/helper/network_call_manager.dart';
import '../../models/chat_message_model.dart';
import '../../models/fetch_patient_list_request_model.dart';
import '../../models/patient_chat_messages_request_model.dart';
import '../../models/vulnerability_criteria_update_model.dart';
import '../../repos/rest_client/rest_client.dart';
import '../../utilities/constants/api_endpoint.dart';
import '../../utilities/constants/app_constants.dart';
import '../../utilities/constants/app_handover_constants.dart';
import '../../utilities/constants/app_size_constants.dart';
class PatientDataProvider {
final RestClient? restClient;
String _sizeParam0 = '0';
String _sizeParam20 = '20';
String _userPreferenceAthmaUrlCodeValue = "UAA_001";
String _patientAndEncounterAthmaUrlCodeValue = "ADT_001";
String _updateAthmaUrlCodeValue = "ADT_003";
String _HTTP_PARAM = "httpMethod";
PatientDataProvider({required this.restClient});
Future<String> getUserPreference() async {
Map<String, dynamic> queryParams = {
ApiParamConstants.athmaUrlCodeKeyParam: _userPreferenceAthmaUrlCodeValue,
ApiParamConstants.athmaUrlContentKeyParam:
ApiParamConstants.vitalSaveAthmaUrlContentValue,
};
Response response = await restClient?.get(
endpoint: ApiEndPoint.athmaUserPreferenceData,
queryParam: queryParams,
isAthmaTokenRequired: true,
);
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
return jsonDecode(response.body)['hospital'] != null
? (jsonDecode(response.body)['hospital']['code'].toString().isNotEmpty
? jsonDecode(response.body)['hospital']['code']
: "")
: "";
} else {
throw ApiException(response.errorBody);
}
}
Future<List<PatientModel>> fetchPatientsBySearchWithWard(
String searchString,
String loginId,
) async {
Map<String, dynamic> queryParams = {
ApiParamConstants.loginParam: loginId,
ApiParamConstants.queryParam: _addPatientsWithWard(
await getUserPreference(),
searchString,
),
ApiParamConstants.sizeParam: ApiParamConstants.sizeParam50,
ApiParamConstants.pageParam: _sizeParam0,
};
Response response = await restClient?.get(
endpoint: ApiEndPoint.addSelectedPatientsWithWard,
queryParam: queryParams,
);
List<PatientModel> patientsList = [];
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
patientsList = (jsonDecode(response.body) as List)
.map((i) => PatientModel.fromJson(i))
.toList();
return patientsList;
} else if (response.statusCode == kErrorState) {
throw FormatException();
} else {
throw ApiException(response.errorBody);
}
}
Future<Map?> searchToReceivePatient(String mrn) async {
Map<String, String> queryParams = {
ApiParamConstants.athmaUrlCodeKeyParam:
_patientAndEncounterAthmaUrlCodeValue,
ApiParamConstants.athmaUrlContentKeyParam:
_receiveBillClearedPatientQuery(mrn),
};
Response response = await restClient?.get(
endpoint: ApiEndPoint.athmaEndPoint,
queryParam: queryParams,
isAthmaTokenRequired: true,
);
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
return response.body.isEmpty
? null
: response.body == "[]"
? null
: json.decode(response.body)[0];
} else {
throw ApiException(response.errorBody);
}
}
Future<bool> receivePatientToWard(Map body) async {
Map<String, String> queryParams = {
ApiParamConstants.athmaUrlCodeKeyParam: _updateAthmaUrlCodeValue,
ApiParamConstants.athmaUrlContentKeyParam:
ApiParamConstants.vitalSaveAthmaUrlContentValue,
_HTTP_PARAM: kHttpMethodPut,
};
Response response = await restClient?.put(
endpoint: ApiEndPoint.saveEndPoint,
body: body,
queryParam: queryParams,
isAthmaTokenRequired: true,
);
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
return true;
} else {
throw ApiException(response.errorBody);
}
}
Future<List<PatientModel>> fetchMyPatients(
FetchPatientListRequestModel fetchPatientListRequestModel,
) async {
Response response = await restClient?.post(
endpoint: ApiEndPoint.fetchMyPatientsList,
body: fetchPatientListRequestModel,
);
List<PatientModel> patientsList = [];
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
patientsList = (jsonDecode(response.body) as List)
.map((i) => PatientModel.fromJson(i))
.toList();
return patientsList;
} else {
throw ApiException(response.errorBody);
}
}
Future<PatientModel?> updateAddSelectedPatients(
String mrn,
String login,
) async {
Response response = await restClient?.get(
endpoint: ApiEndPoint.updateSelectedPatients,
queryParam: {
ApiParamConstants.loginParam: login,
ApiParamConstants.mrnParam: mrn,
},
);
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
return response.body.isEmpty
? null
: PatientModel.fromJson(json.decode(response.body));
} else {
throw ApiException(response.errorBody);
}
}
Future<List<PatientModel>?> updateAddSelectedPatientsToShiftIncharge(
List<String> mrn,
Map<String, dynamic> shiftInchargeInfo,
Map<String, dynamic> staffNurseInfo,
) async {
Map<String, dynamic> body = {
ApiParamConstants.mrnListParam: mrn,
ApiParamConstants.shiftInchargeParam: shiftInchargeInfo,
ApiParamConstants.attendingNurseParam: staffNurseInfo,
};
Response response = await restClient?.post(
endpoint: ApiEndPoint.addSelectedPatientsToShiftIncharge,
body: body,
queryParam: {
"assignEveryOne": [staffNurseInfo.isEmpty ? "false" : "true"],
},
);
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
return response.body.isEmpty
? []
: jsonDecode(response.body)
.map<PatientModel>((json) => PatientModel.fromJson(json))
.toList();
} else {
throw ApiException(response.errorBody);
}
}
Future<SyncChatMessageModel> fetchNewlyAddedPatientsChat(
PatientChatMessagesRequestModel chatMessagesRequestModel,
) async {
Response response = await restClient?.get(
endpoint: ApiEndPoint.fetchNewlyAddedPatientMessages +
"${chatMessagesRequestModel.loginId}" +
ApiParamConstants.slashParam +
"${chatMessagesRequestModel.mrn}",
queryParam: {
ApiParamConstants.pageParam: chatMessagesRequestModel.page,
ApiParamConstants.sizeParam: ApiParamConstants.sizeParam1000,
ApiParamConstants.loadCompleteHistoryParam:
chatMessagesRequestModel.loadCompleteHistory,
},
);
SyncChatMessageModel syncChatMessage = SyncChatMessageModel();
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
syncChatMessage.chatMessages = (jsonDecode(response.body) as List)
.map((i) => ChatMessageModel.fromJson(i))
.toList();
syncChatMessage.xTotalCount =
int.parse(response.headers['x-total-count'] ?? "0");
return syncChatMessage;
} else {
throw ApiException(response.errorBody);
}
}
Future<ChatHistoryModel?> fetchOfflineChatHistory(
FetchPatientListRequestModel offlineChatRequestModel,
int page,
) async {
Response response = await restClient?.post(
endpoint: ApiEndPoint.fetchOfflineChatMessages,
queryParam: {
ApiParamConstants.pageParam: page.toString(),
ApiParamConstants.sizeParam: ApiParamConstants.sizeParam1000,
},
body: offlineChatRequestModel,
);
List<ChatMessageModel> chatMessages = [];
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
chatMessages = (jsonDecode(response.body) as List)
.map((i) => ChatMessageModel.fromJson(i))
.toList();
return ChatHistoryModel(
chatModel: chatMessages,
isPaginationAvailable:
int.parse(response.headers['x-total-count'] ?? "0") > 0
? true
: false,
);
} else if (response.statusCode.status == ResponseHandler.NODATA) {
return null;
} else {
throw ApiException(response.errorBody);
}
}
Future<List<TaskModel>> checkTakeOverRequests(String loginId) async {
Response response = await restClient?.get(
endpoint: ApiEndPoint.checkTakeOverRequests,
queryParam: {
ApiParamConstants.pageParam: "${AppSizeConstants.length0}",
ApiParamConstants.queryParam: _checkTakeoverRequestQuery(loginId),
ApiParamConstants.sizeParam: ApiParamConstants.sizeParam100,
ApiParamConstants.sortParamText: ApiParamConstants.sortIdDescParam,
},
);
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
List<TaskModel> taskDetailsList = [];
if (response.body.isEmpty) {
return taskDetailsList;
} else {
taskDetailsList = jsonDecode(response.body)
.map<TaskModel>((json) => TaskModel.fromJson(json))
.toList();
return taskDetailsList;
}
} else {
throw ApiException(response.errorBody);
}
}
Future<PatientModel?> searchPatientByMRN(String mrn, String lognId) async {
Response response = await restClient?.get(
endpoint: ApiEndPoint.addSelectedPatientsWithWard,
queryParam: {
ApiParamConstants.pageParam: "${AppSizeConstants.length0}",
ApiParamConstants.loginParam: lognId,
ApiParamConstants.queryParam: _searchPatientByMRNQuery(mrn),
ApiParamConstants.sizeParam: _sizeParam20,
},
);
return response.statusCode.status == ResponseHandler.SUCCESSFUL
? response.body.isEmpty
? null
: (jsonDecode(response.body) as List)
.map((i) => PatientModel.fromJson(i))
.toList()
.first
: null;
}
Future<List<TaskModel>> fetchTakeoverAssignedTasks(String ids) async {
Response response = await restClient?.get(
endpoint: ApiEndPoint.taskListEndPoint,
queryParam: {
ApiParamConstants.queryParam:
_fetchTakeoverAssignedTasksRequestQuery(ids),
ApiParamConstants.sizeParam: ApiParamConstants.sizeParam100,
ApiParamConstants.sortParamText: ApiParamConstants.sortIdDescParam,
},
);
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
List<TaskModel> taskDetailsList = [];
taskDetailsList = jsonDecode(response.body)
.map<TaskModel>((json) => TaskModel.fromJson(json))
.toList();
return taskDetailsList;
} else {
throw ApiException(response.errorBody);
}
}
Future<TaskModel?> handoverAcceptOrRejection(TaskModel? taskModel) async {
Response response = await restClient?.post(
endpoint: ApiEndPoint.handoverAcceptOrRejectionRequest,
body: taskModel,
);
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
return response.body.isEmpty
? null
: TaskModel.fromJson(json.decode(response.body));
} else {
throw ApiException(response.errorBody);
}
}
Future<PatientModel?> updateVulnerabilityCriteriaList(
VulnerabilityCriteriaUpdateModel model,
) async {
Response response = await restClient?.post(
endpoint: ApiEndPoint.updateVulnerabilityCriteria,
body: model,
);
if (response.statusCode.status == ResponseHandler.SUCCESSFUL) {
return response.body.isEmpty
? null
: PatientModel.fromJson(json.decode(response.body));
} else {
throw ApiException(response.errorBody);
}
}
/* ---------fetch Takeover requests Query Value----------*/
String _checkTakeoverRequestQuery(String loginId) {
return "active:true AND (taskStatus:INITIATED) AND (assignee.login:$loginId) AND taskDefinition.code:\"" +
kHandoverDefinitionCode +
"\"";
}
/* ---------fetch Takeover Tasks requests Query Value----------*/
String _fetchTakeoverAssignedTasksRequestQuery(String ids) {
return "active:true AND ($ids) AND taskDefinition.code:\"" +
kTaskDefinitionCode +
"\"";
}
/*---------Add Patients using Ward (AthmaUrlQuery)----------*/
String _addPatientsWithWard(
String unitCode,
String searchString,
) {
return "(name:*$searchString* OR mrn:*$searchString* OR location:*$searchString*) AND unitCode:$unitCode AND active:true";
}
String _searchPatientByMRNQuery(String mrn) {
return "(mrn:*$mrn*) AND active:true";
}
String _receiveBillClearedPatientQuery(String mrn) {
return "?query=patientDetails.mrn:$mrn AND status:BILLING_CLEARED";
}
}