Mocking dio : Could not find mocked route matching
I am trying to make unit test with mockitto to test dio.
void main() {
group("Splash init", () {
DioAdapterMockito dioAdapterMockito;
Dio dio;
const path = 'https://..../mobile/';
setUpAll(() {
dioAdapterMockito = DioAdapterMockito();
dio = Dio()..httpClientAdapter = dioAdapterMockito;
});
test(
"call getMainConfigs -> getMainConfig web service called 404 exception",
() async {
final dioError = DioError(
error: {'message': 'Some beautiful error!'},
request: RequestOptions(path: path),
response: Response(
statusCode: 500,
request: RequestOptions(path: path),
),
type: DioErrorType.RESPONSE,
);
dioInterceptor.onPost(path, (request) => request.throws(500, dioError),
headers: {'Content-Type': 'application/json; charset=utf-8'});
expect(() async => await dio.post(path), throwsA(isA<AdapterError>()));
// expect(() async => await dio.get(path), throwsA(isA<DioError>()));
// expect(
// () async => await dio.get(path),
// throwsA(
// predicate(
// (DioError error) =>
// error is DioError &&
// error is AdapterError &&
// error.message == dioError.error.toString(),
// ),
// ),
// );
});
});
}
but after running this test I got this error:
Expected: throws <Instance of 'AdapterError'>
Actual: <Closure: () => Future<Response<dynamic>>>
Which: threw DioError:<DioError [DioErrorType.DEFAULT]: Assertion failed: "Could not find mocked route matching request for https://..../mobile//POST/null/{}/{content-type: application/json; charset=utf-8}"
#0 History.responseBody.<anonymous closure>
package:http_mock_adapter/src/history.dart:32
#1 DioInterceptor.onRequest
package:http_mock_adapter/…/interceptors/dio_interceptor.dart:63
#2 DioMixin._request._interceptorWrapper.<anonymous closure>.<anonymous closure>.<anonymous closure>
package:dio/src/dio.dart:849
#3 DioMixin.checkIfNeedEnqueue
package:dio/src/dio.dart:1121
#4 DioMixin._request._interceptorWrapper.<anonymous closure>.<anonymous closure>
package:dio/src/dio.dart:846
#5 new Future.<anonymous closure> (dart:async/future.dart:174:37)
#6 StackZoneSpecification._run
package:stack_trace/src/stack_zone_specification.dart:208
#7 StackZoneSpecification._registerCallback.<anonymous closure>
package:stack_trace/src/stack_zone_specification.dart:116
I am trying to test exception.
Hi @tazik561 !
The problem is you're throwing a AdapterError as expected value:
expect(() async => await dio.post(path), throwsA(isA<AdapterError>()));
But you was given a DioError into dioAdapterMockito.onPost method:
dioAdapterMockito.onPost(
path,
(request) => request.throws(500, dioError),
headers: {'Content-Type': 'application/json; charset=utf-8'},
);
So I mean you should change your expected value as DioError, like:
expect(() async => await dio.post(path), throwsA(isA< DioError>()));
The full test code: Note: I am using the latest version of the packages
void main() {
group("Splash init", () {
DioAdapterMockito dioAdapterMockito;
Dio dio;
const path = 'https://..../mobile/';
setUpAll(() {
dioAdapterMockito = DioAdapterMockito();
dio = Dio()..httpClientAdapter = dioAdapterMockito;
});
test(
"call getMainConfigs -> getMainConfig web service called 404 exception",
() async {
final dioError = DioError(
error: {'message': 'Some beautiful error!'},
requestOptions: RequestOptions(path: path),
response: Response(
statusCode: 500,
requestOptions: RequestOptions(path: path),
),
type: DioErrorType.response,
);
dioAdapterMockito.onPost(
path,
(request) => request.throws(500, dioError),
headers: {'Content-Type': 'application/json; charset=utf-8'},
);
expect(() async => await dio.post(path), throwsA(isA<AdapterError>()));
// expect(() async => await dio.get(path), throwsA(isA<DioError>()));
// expect(
// () async => await dio.get(path),
// throwsA(
// predicate(
// (DioError error) =>
// error is DioError &&
// error is AdapterError &&
// error.message == dioError.error.toString(),
// ),
// ),
// );
});
});
}
Correct and clean usage of http-mock-adapter should be:
void main() {
DioAdapter dioAdapterMockito;
Dio dio;
DioError dioError;
const path = 'https://example.com';
setUpAll(() {
dioAdapterMockito = DioAdapterMockito();
dio = Dio();
dio.httpClientAdapter = dioAdapterMockito;
DioError(
error: {'message': 'Some beautiful error!'},
requestOptions: RequestOptions(path: path),
response: Response(
statusCode: 500,
requestOptions: RequestOptions(path: path),
),
type: DioErrorType.response,
);
});
group("Splash init", () {
test(
"call getMainConfigs -> getMainConfig web service called 404 exception",
() async {
dioAdapterMockito.onPost(
path,
(request) => request.throws(500, dioError),
headers: {'Content-Type': 'application/json; charset=utf-8'},
);
expect(() async => await dio.post(path), throwsA(isA<DioError>()));
});
});
}
Hi @tazik561 !
The problem is you're throwing a
AdapterErroras expected value:expect(() async => await dio.post(path), throwsA(isA<AdapterError>()));But you was given a
DioErrorintodioAdapterMockito.onPostmethod:dioAdapterMockito.onPost( path, (request) => request.throws(500, dioError), headers: {'Content-Type': 'application/json; charset=utf-8'}, );So I mean you should change your expected value as
DioError, like:expect(() async => await dio.post(path), throwsA(isA<AdapterError>()));The full test code: Note: I am using the latest version of the packages
void main() { group("Splash init", () { DioAdapterMockito dioAdapterMockito; Dio dio; const path = 'https://..../mobile/'; setUpAll(() { dioAdapterMockito = DioAdapterMockito(); dio = Dio()..httpClientAdapter = dioAdapterMockito; }); test( "call getMainConfigs -> getMainConfig web service called 404 exception", () async { final dioError = DioError( error: {'message': 'Some beautiful error!'}, requestOptions: RequestOptions(path: path), response: Response( statusCode: 500, requestOptions: RequestOptions(path: path), ), type: DioErrorType.response, ); dioAdapterMockito.onPost( path, (request) => request.throws(500, dioError), headers: {'Content-Type': 'application/json; charset=utf-8'}, ); expect(() async => await dio.post(path), throwsA(isA<AdapterError>())); // expect(() async => await dio.get(path), throwsA(isA<DioError>())); // expect( // () async => await dio.get(path), // throwsA( // predicate( // (DioError error) => // error is DioError && // error is AdapterError && // error.message == dioError.error.toString(), // ), // ), // ); }); }); }Correct and clean use of http-mock-adapter should be:
void main() { DioAdapter dioAdapterMockito; Dio dio; DioError dioError; const path = 'https://example.com'; setUpAll(() { dioAdapterMockito = DioAdapterMockito(); dio = Dio(); dio.httpClientAdapter = dioAdapterMockito; DioError( error: {'message': 'Some beautiful error!'}, requestOptions: RequestOptions(path: path), response: Response( statusCode: 500, requestOptions: RequestOptions(path: path), ), type: DioErrorType.response, ); }); group("Splash init", () { test( "call getMainConfigs -> getMainConfig web service called 404 exception", () async { dioAdapterMockito.onPost( path, (request) => request.throws(500, dioError), headers: {'Content-Type': 'application/json; charset=utf-8'}, ); expect(() async => await dio.post(path), throwsA(isA<DioError>())); }); }); }
Thank . I changed my code to this:
group("Dio Exception", () {
DioAdapterMockito dioAdapterMockito;
Dio dio;
DioError dioError;
const path = 'https://..../mobile/';
setUpAll(() {
dioAdapterMockito = DioAdapterMockito();
dio = Dio()..httpClientAdapter = dioAdapterMockito;
dioError = DioError(
error: {'message': 'Some beautiful error!'},
request: RequestOptions(
path: path,
),
response: Response(
statusCode: 500,
request: RequestOptions(path: path),
),
type: DioErrorType.RESPONSE,
);
});
test(
"call getMainConfigs -> getMainConfig web service called 404 exception",
() async {
dioAdapterMockito.onPost(
path,
(request) => request.throws(500, dioError),
headers: {'Content-Type': 'application/json; charset=utf-8'},
);
var result = await dio.post(path, data: null);
expect(() async => result, throwsA(isA<DioError>()));
});
});
}
But I got this error:
DioError [DioErrorType.DEFAULT]: NoSuchMethodError: The getter 'headers' was called on null.
Receiver: null
Tried calling: headers
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 DioMixin._dispatchRequest
package:dio/src/dio.dart:927
<asynchronous suspension>
#2 StackZoneSpecification._registerUnaryCallback.<anonymous closure> (package:stack_trace/src/stack_zone_specification.dart)
package:stack_trace/src/stack_zone_specification.dart:1
<asynchronous suspension>
2
DioMixin._dispatchRequest
package:dio/src/dio.dart:966
@tazik561 are you using the latest version of http_mock_adapter?
@tazik561
Don't create a variable for await dio.post() just write it directly.
However expect method should be:
expect(() async => await dio.post(path, data: null),
throwsA(isA<DioError>()));
Not this:
var result = await dio.post(path, data: null);
expect(() async => result, throwsA(isA<DioError>()));
And make sure you're using latest versions of dio and http_mock_adapter.
Here is the full code of your test:
group("Dio Exception", () {
DioAdapterMockito dioAdapterMockito;
Dio dio;
DioError dioError;
const path = 'https://..../mobile/';
setUpAll(() {
dioAdapterMockito = DioAdapterMockito();
dio = Dio()..httpClientAdapter = dioAdapterMockito;
dioError = DioError(
error: {'message': 'Some beautiful error!'},
requestOptions: RequestOptions(path: path),
response: Response(
statusCode: 500,
requestOptions: RequestOptions(path: path),
),
type: DioErrorType.response,
);
});
test(
"call getMainConfigs -> getMainConfig web service called 404 exception",
() async {
dioAdapterMockito.onPost(
path,
(request) => request.throws(500, dioError),
headers: {'Content-Type': 'application/json; charset=utf-8'},
);
expect(() async => await dio.post(path, data: null),
throwsA(isA<DioError>()));
});
});
Of course not. I am using dio: ^3.0.9 and http_mock_adapter: ^0.1.6 because I don't use sounds null safety environment: sdk: ">=2.7.0 <3.0.0".
It is possible to mix DioAdapterMockito in this way ):
setupMockHttpClientFailure404(){
dioAdapterMockito.onPost(
path,
(request) => request.throws(404, dioError),
headers: {'Content-Type': 'application/json; charset=utf-8'},
);
}
test(
'should throw a ServerException when the responce code is 404 or other ',
() async {
setupMockHttpClientFailure404();
final call = dataSource.getConcreteNumberTrivia;
expect(call(url), throwsA(TypeMatcher<ServerExceptions>()));
},
);
main method on repo class:
Future<NumberTriviaModel> getConcreteNumberTrivia(String url) async {
final response =
await dio.get(url, headers: {'Content-Type': 'application/json'});
if (response.statusCode == 200) {
return NumberTriviaModel.fromJson(json.decode(response.body));
} else {
throw ServerExceptions();
}
}
to check a method that has dio method inside it?
Hi @tazik561
I tried fix your problem by initializing dio: ^3.0.9 and http_mock_adapter: ^0.1.6.
And I fixed it, It's simple you just need to change your expected value to:
expect(() async => await dio.post(path), throwsA(isA<DioError>()));
Just that, and I said that in this answer.
@theiskaa i guess we resolved this in #100 PR, right?
@LukaGiorgadze no Luka, we were resolved this issue with #100 PR. @tazik561 's issue needn't any changes on http-mock-adapter, he just should do this , so the problem is his test code.
Hi @tazik561 I tried fix your problem by initializing
dio: ^3.0.9andhttp_mock_adapter: ^0.1.6. And I fixed it, It's simple you just need to change your expected value to:expect(() async => await dio.post(path), throwsA(isA<DioError>()));Just that, and I said that in this answer.
I don't want to test dio directly . As I mention above I have a method called : getConcreteNumberTrivia.
Future<NumberTriviaModel> getConcreteNumberTrivia(String url) async {
final response =
await dio.get(url, headers: {'Content-Type': 'application/json'});
if (response.statusCode == 200) {
return NumberTriviaModel.fromJson(json.decode(response.body));
} else {
throw ServerExceptions();
}
}
I am trying write test for this method getConcreteNumberTrivia. Inside this method there is a dio .Now I want when parser rich to dio, dio throw an error or exception .
In this answer we just test dio directly. But I want to test a method that has dio part like repository layer.
for example
setupMockHttpClientFailure404(){
dioAdapterMockito.onPost(
path,
(request) => request.throws(404, dioError),
headers: {'Content-Type': 'application/json; charset=utf-8'},
);
}
test(
'should throw a ServerException when the responce code is 404 or other ',
() async {
setupMockHttpClientFailure404();
final call = dataSource.getConcreteNumberTrivia;
expect(call(url), throwsA(TypeMatcher<ServerExceptions>()));
},
);
I call final call = dataSource.getConcreteNumberTrivia; in test, In getConcreteNumberTrivia method when parser rich to final response = await dio.get(url, headers: {'Content-Type': 'application/json'}); , throw an exception.
same problem here
Well, I'm trying to use this with get_it. I register my client with:
getIt.registerLazySingleton(
() => BaseOptions(baseUrl: 'https://www.bscotch.net/api/levelhead'),
instanceName: 'rumpusClientBaseOptions');
getIt.registerLazySingleton(
() => Dio(getIt.get(instanceName: 'rumpusClientBaseOptions')),
instanceName: 'rumpusClient');
Then in setUpAll, I do:
setUpAll(() async {
await setUpDI();
var adapter = DioAdapter();
adapter
..onGet(
'/players',
(request) => request.reply(200, {
'data': [
{
"_id": "609e5516f0b9d200b711b8b5",
"userId": "pvdw78",
"stats": {
"Subscribers": 0,
"NumFollowing": 0,
"Crowns": 0,
"Shoes": 0,
"PlayTime": 0,
"TipsPerLevel": 0,
"TipsPerDay": 0,
"TippedPerLevelPlayed": 0,
"TippedPerDay": 0,
"HiddenGem": 0,
"Trophies": 0,
"PerkPoints": 5,
"CampaignProg": 0,
"TimeTrophies": 0
},
"createdAt": "2021-05-14T10:46:46.723Z",
"updatedAt": "2021-05-14T10:46:48.639Z",
"alias": {
"userId": "pvdw78",
"alias": "LeapyimbleZiprompa",
"avatarId": "gr18-serious",
"context": "levelhead"
}
}
]
}));
Dio client = getIt.get(instanceName: 'rumpusClient');
client.httpClientAdapter = adapter;
});
Since it is a lazy singleton, I'm sure I get the only one instance of Dio. I change the adapter to this mock adapter as you can see above. However, running my tests, I get the error saying:
DioError [DioErrorType.other]: Assertion failed: "Could not find mocked route matching request for /players/GET/null/{includeRecords: true, includeStats: true, userIds: foobar}/{}"
Opting out of baseUrl also does no good.
Environment
> flutter --version
Flutter 2.0.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision adc687823a (4 weeks ago) • 2021-04-16 09:40:20 -0700
Engine • revision b09f014e96
Tools • Dart 2.12.3
- dio 4.0.0
- http_mock_adapter 0.2.1
Hi @erayerdin first of all thanks for your response!
In your test code I can see you did mocking some request by this path:
/players/GET/{your data here}/{includeRecords: true, includeStats: true, userIds: foobar}/{}
and you get error by this path:
/players/GET/null/{includeRecords: true, includeStats: true, userIds: foobar}/{}
So your test code's path and real code's path are different. However in your real code you didn't give data and that's why your data's place is null. Just try giving same data, path and variables, if something goes wrong please tell us, write down in this issue or fill a new one.
Hi @tazik561 I tried fix your problem by initializing
dio: ^3.0.9andhttp_mock_adapter: ^0.1.6. And I fixed it, It's simple you just need to change your expected value to:expect(() async => await dio.post(path), throwsA(isA<DioError>()));Just that, and I said that in this answer.
I don't want to test dio directly . As I mention above I have a method called :
getConcreteNumberTrivia.Future<NumberTriviaModel> getConcreteNumberTrivia(String url) async { final response = await dio.get(url, headers: {'Content-Type': 'application/json'}); if (response.statusCode == 200) { return NumberTriviaModel.fromJson(json.decode(response.body)); } else { throw ServerExceptions(); } }I am trying write test for this method
getConcreteNumberTrivia. Inside this method there is a dio .Now I want when parser rich to dio, dio throw an error or exception .In this answer we just test dio directly. But I want to test a method that has dio part like repository layer.
for example
setupMockHttpClientFailure404(){ dioAdapterMockito.onPost( path, (request) => request.throws(404, dioError), headers: {'Content-Type': 'application/json; charset=utf-8'}, ); } test( 'should throw a ServerException when the responce code is 404 or other ', () async { setupMockHttpClientFailure404(); final call = dataSource.getConcreteNumberTrivia; expect(call(url), throwsA(TypeMatcher<ServerExceptions>())); }, );I call
final call = dataSource.getConcreteNumberTrivia;in test, IngetConcreteNumberTriviamethod when parser rich tofinal response = await dio.get(url, headers: {'Content-Type': 'application/json'});, throw an exception.
@tazik561 Okay, as I understand it, you have a dio and that one isn't into your method right? So you wanna test your method, then you shouldn't test dio directly, yeah that is correct. But it's interesting where you do set dio class? you have to do same thing what are you doing when you are testing dio directly. Just set you dio's httpClientAdapter and go on!
I have had several cases where the mocked route could not be found. It was always my mistake but it is hard to trace down with the current assertion errors. I suggest we introduce a custom error messages with diffs for data/headers/params, especially if there is only one mocked response. With more than one mocked response it might get a bit harder to generate a correct diff.
Hi guys! I'm trying to solve this and there is no way! I have this class:
import 'dart:io';
import 'package:dio/dio.dart';
/// Dio requests types
enum Method {
/// post
post,
/// get
get,
/// put
put,
/// delete
delete,
/// patch
patch,
}
/// {@template http_service}
/// A service for managing requests
/// {@endtemplate}
class HttpService {
/// {@macro http_service}
HttpService({
required Dio httpClient,
}) : _httpClient = httpClient {
init(httpClient: _httpClient);
}
late Dio _httpClient;
/// service initialization and configuration
Future<HttpService> init({
required Dio httpClient,
}) async {
_httpClient = httpClient;
return this;
}
/// help in handling request methods
Future<Response> request({
required String endpoint,
required Method method,
Map<String, dynamic>? params,
}) async {
Response response;
try {
if (method == Method.post) {
response = await _httpClient.post<dynamic>(endpoint, data: params);
} else if (method == Method.delete) {
response = await _httpClient.delete<dynamic>(endpoint);
} else if (method == Method.patch) {
response = await _httpClient.patch<dynamic>(endpoint);
} else {
response =
await _httpClient.get<dynamic>(endpoint, queryParameters: params);
}
if (response.statusCode == 200) {
return response;
} else if (response.statusCode == 401) {
throw Exception('Unauthorized');
} else if (response.statusCode == 500) {
throw Exception('Server Error');
} else {
throw Exception("Something does wen't wrong");
}
} on SocketException catch (e) {
throw Exception('Not Internet Connection');
} on FormatException catch (e) {
throw Exception('Bad response format');
} on DioError catch (e) {
throw Exception(e);
} catch (e) {
throw Exception("Something wen't wrong");
}
}
}
And trying to test errors and exceptions with this:
test('should return a Server Error Exception', () async {
dioAdapter.onPost(
'/endpoint',
(server) => server.throws(500, Constants.dioError),
data: <String, dynamic>{},
headers: Constants.edamamPostHeader,
);
expect(
() async => await httpService.request(
endpoint: '/endpoint',
method: Method.post,
params: <String, dynamic>{},
),
isA<DioError>,
);
});
But could not resolve correctly the expectation:
Expected: <Closure: () => TypeMatcher<DioError> from Function 'isA': static.>
Actual: <Closure: () => Future<Response<dynamic>>>
Any thoughts? Thanks in advance!
I did this which I think is correct. What do you think?
