http-mock-adapter Dio Assertion failed: "Could not find mocked route matching request for - flutter

I'm trying to mock my Dio client with http-mock-adapter on put method and error method but I get this errors:
Exception: Assertion failed: "Could not find mocked route matching request for POST /onboard/answer { data: {"pageId":"1"},
Exception: DioError [DioErrorType.response]: {message: Some beautiful error!}
I found this issue https://githubmemory.com/repo/lomsa-dev/http-mock-adapter/issues/96?page=2 but I didn't solve my problem. Below is the code
main() {
final dio = Dio(BaseOptions());
final dioAdapter = DioAdapter(dio: dio);
dio.httpClientAdapter = dioAdapter;
final service = NetworkService(dio);
test("should return a DioError", () async {
const path = "/onboard/page-date/0/lastAnswerId";
final dioError = DioError(
error: {'message': 'Some beautiful error!'},
requestOptions: RequestOptions(path: path),
response: Response(
statusCode: 404,
requestOptions: RequestOptions(path: path),
),
type: DioErrorType.response,
);
dioAdapter.onGet(path, (server) {
server.throws(404, dioError);
});
final result = await service.getOnboardingAnswer("lastAnswerId");
expect(result, isA<OnboardModel>());
});
final answerModel = AnswerModel(pageId: "1");
test("should return 200", () async {
const path = "/onboard/answer";
dioAdapter.onPost(path, (server) {
server.reply(200, 200);
});
final result = await service.postOnboardingAnswer(answerModel);
expect(result, isA<int>());
});

instead of final dioAdapter = DioAdapter(dio: dio);
use final dioAdapter = DioAdapter(dio: dio, matcher: const UrlRequestMatcher()); this line only match passed URL not entire http request, so it should work on your case
in this example from official lib your default adapter using FullHttpRequestMatcher which means that it will match your entire request including passed POST parameter

I had the same issue. Dio fails because it tries to match route with the exact data payload you are sending.
If you don't care about the actual data being sent you can use Matchers.any from http-mock-adapter lib:
dioAdapter.onPost(url, (server) {
server.reply(200, 200);
}, data: Matchers.any);

Related

Missing stub error on Mockito in Flutter. Trying to use post of Dio

I’m trying to perform a post request using Dio and test it with Mockito. But I don’t know why this error is happening, someone knows what is this?
Error on the tests:
User Profile Image should verify if post method is successfully called [E]
MissingStubError: 'post'
No stub was found which matches the arguments of this method call:
post('/CadastroUsuario/test#example.com/salvarFotoPerfil', {data: Instance of 'FormData', queryParameters: null, options: null, cancelToken: null, onSendProgress: null, onReceiveProgress: null})
Add a stub for this method using Mockito's 'when' API, or generate the mock for MockDio with 'returnNullOnMissingStub: true'.
package:mockito/src/mock.dart 190:7 Mock._noSuchMethod
package:mockito/src/mock.dart 184:45 Mock.noSuchMethod
test/data/c_user_registration_data_source_test.mocks.dart 147:14 MockDio.post
package:app_test/data/c_user_registration_data_source.dart 70:33 CUserRegistrationDataSource.setUserProfileImage
My test code:
group('User Profile Image', () {
const email = 'test#example.com';
var imagePath = File('test/assets/images/profile_image.png').path;
var expectedUrl = uploadUserProfilePhotoPath.replaceFirst('{email}', email);
test('should verify if post method is successfully called', () async {
var formData = FormData.fromMap({
'profileImage': await MultipartFile.fromFile(imagePath),
});
when(dio.post(
expectedUrl,
data: formData,
)).thenAnswer(
(_) async => Response(
data: null,
statusCode: 200,
requestOptions: CommomMocks.getRequestOptions(expectedUrl),
),
);
await dataSource.setUserProfileImage(
email: email,
imagePath: imagePath,
);
verify(dio.post(expectedUrl, data: formData)).called(1);
});
});
Implementation:
#override
Future<void> setUserProfileImage({
required String email,
required String imagePath,
}) async {
final http = _httpClientApp.instance();
var apiUrl = uploadUserProfilePhotoPath.replaceFirst('{email}', email);
var formData = FormData.fromMap({
'profileImage': await MultipartFile.fromFile(imagePath),
});
final response = await http.post(apiUrl, data: formData);
return _handleResponse(response);
}
It shows that method post from the mocked Dio is missing. I already run the pub command to generate the mocks, and I make the stub before calling the implementation method. Is there something I'm missing?
Thanks in advance.
I think the problem was in the “FormData” instance in the prod function:
var formData = FormData.fromMap({
'profileImage': await MultipartFile.fromFile(imagePath),
});
Every time the test runs, a new instance is created and the stub is not the same.
So the solution I found, is to use a type matcher to check if any parameter of type 'FormData' is called with the function.
test('should verify if post method is successful called', () async {
httpStubs.setUpMockHttpClientAppSuccess(expectedUrl, formData);
await dataSource.setUserProfileImage(
email: email,
imagePath: imagePath,
);
verify(
() => httpClientApp.post(
expectedUrl,
body: isA<FormData>(),
),
).called(1);
});
The stub function:
void setUpMockHttpClientAppSuccess(String url, dynamic payload) {
when(
() => httpClientApp.post(
any(),
body: any(named: 'body'),
),
).thenAnswer(
(_) async => Response(
data: null,
statusCode: 200,
requestOptions: CommomMocks.getRequestOptions(url),
),
);
}
I'm not sure if the solution is the best or the best practice, therefore this resolves my test problem. If someone knows a better solution, please tell me.
Thanks to all!! :)

Why sending the following request ends up with uncaught exception?

I have the following Flutter & Dart code function, which sends a request to the server:
Future<void> autoAuth(BuildContext ctx) async {
final url = Uri.parse('${this._baseURL.toString()}/auto-auth');
try {
final deviceStorage = await SharedPreferences.getInstance();
if (deviceStorage.getString('refreshToken') == null) {
return this._setUser(null);
}
final response = await http.post(url, headers: {
'Authorization': 'Bearer ${deviceStorage.getString('refreshToken')!}',
}).timeout(const Duration(seconds: 3));
final Map<String, dynamic> responseBody = json.decode(response.body);
if (responseBody['success']) {
this._refreshAccessToken(ctx, deviceStorage);
return this._setUser(new User(
id: responseBody['data']['id'],
isSubscribed: responseBody['data']['isSubscribed'],
playlistId: responseBody['data']['playlistId'],
));
}
this._setUser(null);
} on SocketException {
this._setUser(null);
throw Error();
} on TimeoutException {
this._setUser(null);
throw Error();
} catch (_) {
this._setUser(null);
}
}
Note, that url is wrong intentionally, so the request will timeout.
But, for this, I coded: .timeout(...) on the future request. So, basically, after 3 secnods it should caught by on TimeoutException exception catch.
It does so. However, after something like 1 minute (probably some default timeout of http request in dart), I get an uncaught exception because the request has timed-out. Where Am I wrong?
This is because you are using it in the wrong way. The .timeout code you use, is generic timeout for any future. Thus, you catch the error of the future timeout, but you don't catch the error being generated from the http request timeout.
To use it correctly, first add the following import: import 'package:http/io_client.dart' as http;
Then change the code to:
final ioClient = HttpClient();
ioClient.connectionTimeout = const Duration(seconds: 3);
final client = http.IOClient(ioClient);
final response = await client.post(url, headers: {
'Authorization': 'Bearer ${deviceStorage.getString('refreshToken')!}',
});

Flutter how to unit test Dio 401, 500 responses

Currently I'm testing 200 responses as following
final dio = Dio();
final dioAdapter = DioAdapter();
dio.httpClientAdapter = dioAdapter;
const path = 'https://endpoint.com';
test('Loading shows when user taps set up trading account', () async {
dioAdapter
..onPost(
path,
(request) => request.reply(204, {}),
);
final onGetResponse = await dio.post(path);
when(_tradingAccountService.setUpTradingAccount())
.thenAnswer((realInvocation) => Stream.value(HttpResponse(onGetResponse.data, onGetResponse)));
await signUpViewModel.setUpTradingAccount();
expect(signUpViewModel.isSettingUpTradingAccount, true);
});
But when I try to test 401 or 500, dio throws HttpStatusError
final dioError = DioError(
error: {'message': 'Some beautiful error!'},
requestOptions: RequestOptions(path: '/foo'),
response: Response(
statusCode: 500,
requestOptions: RequestOptions(path: '/foo'),
),
type: DioErrorType.response,
);
dioAdapter.onPost(
path,
(request) => request.throws(500, dioError),
);
I was hoping to do something like this
when(_tradingAccountService.setUpTradingAccount())
.thenAnswer((realInvocation) => Stream.value(HttpResponse(204)));
Those didn't help How create test for dio timeout and
https://github.com/flutterchina/dio/blob/master/dio/test/mock_adapter.dart
Use http_mock_adapter,package for mocking Dio requests.
You can simply replace your injected Dio's httpClientAdapter with DioAdapter() of http_mock_adapter:
example from examples of http_mock_adapter
Usage
Here is the basic usage scenario of the package (via DioAdapter):
import 'package:dio/dio.dart';
import 'package:http_mock_adapter/http_mock_adapter.dart';
void main() async {
final dio = Dio();
final dioAdapter = DioAdapter();
dio.httpClientAdapter = dioAdapter;
const path = 'https://example.com';
dioAdapter
..onGet(
path,
(request) => request.reply(200, {'message': 'Successfully mocked GET!'}),
)
..onGet(
path,
(request) => request.reply(200, {'message': 'Successfully mocked POST!'}),
);
final onGetResponse = await dio.get(path);
print(onGetResponse.data); // {message: Successfully mocked GET!}
final onPostResponse = await dio.post(path);
print(onPostResponse.data); // {message: Successfully mocked POST!}
}
Note:
There was a fixed kinda issue please take a look at it there, I guess it would be helpful.

How to put this dart class together so I can use it in different parts of my code

I am trying to implement a Dio interceptor so I will be able to use it with my code.
I will be using this interceptor in many places. So I thought it would make sense to put it into a class or an interface whichever is best and just extend or implement it my subclasses.
I have this so far:
class AppInterceptor {
Dio dio = Dio();
AppInterceptor() {
dio.interceptors
.add(InterceptorsWrapper(onRequest: (Options options) async {
var token = await getAuthorizationToken();
options.headers["Authorization"] = 'Bearer $token';
return options;
}, onResponse: (Response response) {
// Finally, the string is parsed into a JSON object.
//print(response.toString());
return response;
}, onError: (DioError e) {
print('somthing went wrong');
// Do something with response error
return e; //continue
}));
}
}
How do I use this in a subclass to make an http call?
I tried something like this when trying to do my http call:
Response response = await AppInterceptor.dio.post(Global.functionsUrl+'/auth/linkuseraccount/', data: {'hey': 'hello'});
print(response);
It fails each time with Unhandled Exception: DioError [DioErrorType.RESPONSE]: Http status error [403]
From my backend, I can tell that it fails because the interceptor didn't pass in the authentication header.
How should I go about this?

I/flutter (23942): DioError [DioErrorType.RESPONSE]: Http status error [500]

I'm facing HTTP error 500 while Authenticating or log in with API.
and Unable to get this error.
it was working nicely before but suddenly throwing me this HTTP error.
CODE:
The instance of Dio class:
dio() {
Dio dio = Dio();
dio.options.connectTimeout = 60000; //5s
dio.options.receiveTimeout = 60000;
return dio;
}
authenticating method:
Future<Map> authenticate({#required String username, #required String password}) async{
String url = "https://.....";
Response response;
try{
response =await dio().post(
url,
options: Options(
contentType: ContentType.parse("application/x-www-form-urlencoded"),
),
data: {
'grant_type': 'password',
'client_id':clientId,
'client_secret':clientSecret,
'username': username,
'password': password,
}
);
print("Authentication post response.dataaaaaa:${response.data}");
return response.data;
}catch(e){
print("ERROR:$e");
throw e;
}
}
Getting error in catch bloc:
DioError [DioErrorType.RESPONSE]: Http status error [500]
Http status code 500 means something wrong from your API backend?