Flutter - type 'Null' is not a subtype of type 'Future<Response<dynamic>>' when mocking Dio's get method
284
Finally I've got a working solution.
It seems that Dio needs a seperate mock library to mock itself using adapters. The library to use is http_mock_adapter: ^0.1.4
and its below is its link.
LINK: Http Mock Library
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:http_mock_adapter/http_mock_adapter.dart';
import 'package:test/test.dart';
void main() async {
// How to mock with DioAdapter
group('DioAdapter usage', () {
// Creating dio instance for mocking.
// For instance: you can use your own instance from injection and replace
// dio.httpClientAdapter with mocker DioAdapter
const path = 'https://example.com';
test('Expects Dioadapter to mock the data', () async {
final dio = Dio();
final dioAdapter = DioAdapter();
dio.httpClientAdapter = dioAdapter;
dioAdapter
.onGet(path)
.reply(200,
{'message': 'Successfully mocked GET!'}) // only use double quotes
.onPost(path)
.reply(200, {'message': 'Successfully mocked POST!'});
// Making dio.get request on the path an expecting mocked response
final getResponse = await dio.get(path);
expect(jsonEncode({'message': 'Successfully mocked GET!'}),
getResponse.data);
// Making dio.post request on the path an expecting mocked response
final postResponse = await dio.post(path);
expect(jsonEncode({'message': 'Successfully mocked POST!'}),
postResponse.data);
});
// Alternatively you can use onRoute chain to pass custom requests
test('Expects Dioadapter to mock the data with onRoute', () async {
final dio = Dio();
final dioAdapter = DioAdapter();
dio.httpClientAdapter = dioAdapter;
dioAdapter
.onRoute(path, request: Request(method: RequestMethods.PATCH))
.reply(200, {
'message': 'Successfully mocked PATCH!'
}) // only use double quotes
.onRoute(path, request: Request(method: RequestMethods.DELETE))
.reply(200, {'message': 'Successfully mocked DELETE!'});
// Making dio.get request on the path an expecting mocked response
final patchResponse = await dio.patch(path);
expect(jsonEncode({'message': 'Successfully mocked PATCH!'}),
patchResponse.data);
// Making dio.post request on the path an expecting mocked response
final deleteResposne = await dio.delete(path);
expect(jsonEncode({'message': 'Successfully mocked DELETE!'}),
deleteResposne.data);
});
});
// Also, for mocking requests, you can use dio Interceptor
group('DioInterceptor usage', () {
// Creating dio instance for mocking.
// For instance: you can use your own instance from injection and add
// DioInterceptor in dio.interceptors list
final dioForInterceptor = Dio();
final dioInterceptor =
DioInterceptor(); // creating DioInterceptor instance for mocking requests
dioForInterceptor.interceptors.add(dioInterceptor);
const path = 'https://example2.com';
test('Expects Dioadapter to mock the data', () async {
// Defining request types and their responses respectively with their paths
dioInterceptor
.onDelete(path)
.reply(200,
{'message': 'Successfully mocked GET!'}) // only use double quotes
.onPatch(path)
.reply(200, {'message': 'Successfully mocked POST!'});
// Making dio.delete request on the path an expecting mocked response
final getResponse = await dioForInterceptor.delete(path);
expect(jsonEncode({'message': 'Successfully mocked GET!'}),
getResponse.data);
// Making dio.patch request on the path an expecting mocked response
final postResposne = await dioForInterceptor.patch(path);
expect(jsonEncode({'message': 'Successfully mocked POST!'}),
postResposne.data);
});
});
group('Raising the custrom Error onRequest', () {
const path = 'https://example.com';
test('Test that throws raises custom exception', () async {
final dio = Dio();
final dioAdapter = DioAdapter();
dio.httpClientAdapter = dioAdapter;
const type = DioErrorType.RESPONSE;
final response = Response(statusCode: 500);
const error = 'Some beautiful error';
// Building request to throw the DioError exception
// on onGet for the specific path
dioAdapter.onGet(path).throws(
500,
DioError(
type: type,
response: response,
error: error,
),
);
// Checking that exception type can match `AdapterError` type too
expect(() async => await dio.get(path),
throwsA(TypeMatcher<AdapterError>()));
// Checking that exception type can match `DioError` type too
expect(() async => await dio.get(path), throwsA(TypeMatcher<DioError>()));
// Checking the type and the message of the exception
expect(
() async => await dio.get(path),
throwsA(
predicate((DioError e) => e is DioError && e.message == error)));
});
});
}
Author by
Ariel
Updated on January 02, 2023Comments
-
Ariel over 1 year
I am trying to mock Dio's get method. The mock is working fine as per my test. However, when calling inside the test type '
Null
' is not a subtype of type 'Future<Response<dynamic>>
'.I have called
newsApi.get('/top-headlines')
during test as well. And, I can assure it that the mock is returning data fine. But for unknown reasons, the call insideNewsService
is givingnull
. Could you please guide me in solving the issue?Filename: services/news.dart
import 'dart:io'; import 'package:dio/dio.dart'; import 'package:newsapp/enums/news_category.dart'; import 'package:newsapp/enums/news_country.dart'; import 'package:newsapp/models/articles.dart'; import 'package:newsapp/models/error.dart'; import '../main.dart'; class NewsService { final Dio newsApi; NewsService({required this.newsApi}); Future<dynamic> getArticlesByCategory( NewsCategory category, { int page = 1, int pageSize = 100, NewsCountry country = NewsCountry.US, }) async { final response = await newsApi.get('top-headlines', queryParameters: { 'category': category.name, 'country': country.name.toLowerCase(), 'page': page, 'pageSize': pageSize, }); print(response); await newsApi.get('top-headlines', queryParameters: { 'category': category.name, 'country': country.name.toLowerCase(), 'page': page, 'pageSize': pageSize, }).then((response) { if (response.statusCode == HttpStatus.ok) { if (response.data['status'] == 'ok') { return Articles.fromJson(response.data); } else { return Error.fromJson(response.data); } } else if (response.statusCode == HttpStatus.unauthorized) { return Error.fromJson(response.data); } else { return Future.error( 'Failure processing request. Please try again later.'); } }, onError: (error) { print(error); logger.e(error); return Future.error(error); }).catchError((error) { print(error); logger.e(error); return error; }); } }
Filename: test/news.dart
import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:newsapp/enums/news_category.dart'; import 'package:newsapp/models/articles.dart'; import 'package:newsapp/services/news.dart'; import '../mocks/dio.dart'; void main() async { group('NewsService tests', () { //Arrange late MockDio newsApi; group('NewsService.getArticles() tests', () { setUp(() { newsApi = MockDio(); Future<Response> responseMethod = Future.value(Response( data: { "status": "ok", "totalResults": 11207, "articles": [ { "source": {"id": "bbc-news", "name": "BBC News"}, "author": "https://www.facebook.com/bbcnews", "title": "Indian PM Modi's Twitter hacked with bitcoin tweet", "description": "The Indian prime minister's account had a message stating that bitcoin would be distributed to citizens.", "url": "https://www.bbc.co.uk/news/world-asia-india-59627124", "urlToImage": "https://ichef.bbci.co.uk/news/1024/branded_news/5998/production/_122063922_mediaitem122063921.jpg", "publishedAt": "2021-12-12T10:59:57Z", "content": "Image source, AFP via Getty Images\r\nImage caption, Modi has has more than 70 million Twitter followers\r\nIndian Prime Minister Narendra Modi's Twitter account was hacked with a message saying India ha… [+854 chars]" }, { "source": {"id": null, "name": "New York Times"}, "author": "Corey Kilgannon", "title": "Why New York State Is Experiencing a Bitcoin Boom", "description": "Cryptocurrency miners are flocking to New York’s faded industrial towns, prompting concern over the environmental impact of huge computer farms.", "url": "https://www.nytimes.com/2021/12/05/nyregion/bitcoin-mining-upstate-new-york.html", "urlToImage": "https://static01.nyt.com/images/2021/11/25/nyregion/00nybitcoin5/00nybitcoin5-facebookJumbo.jpg", "publishedAt": "2021-12-06T00:42:28Z", "content": "The plant opening northeast of Niagara Falls this month, in Somerset, N.Y., is part of a \$550 million project by Terawulf, a Bitcoin mining company. The project also includes a proposed 150-megawatt … [+1514 chars]" } ] }, statusCode: HttpStatus.ok, requestOptions: RequestOptions(path: '/top-headlines'))); when(() => newsApi.get( '/top-headlines', queryParameters: any(named: 'queryParameters'), options: any(named: 'options'), cancelToken: any(named: 'cancelToken'), onReceiveProgress: any(named: 'onReceiveProgress'), )).thenAnswer((_) => responseMethod); }); tearDown(() { reset(newsApi); }); test('Get Articles', () async { // Arrange NewsService newsService = NewsService(newsApi: newsApi); final response = await newsApi.get('/top-headlines'); print('Response'); print(response.data); final articles = Articles.fromJson(response.data); print(articles); // Act await newsService.getArticlesByCategory(NewsCategory.business); // Assert verify(() => newsApi.get('/top-headlines', queryParameters: any(named: 'queryParameters'))).called(1); }); }); }); }
-
Ali Akber about 2 yearsWere you able to fix this? I'm facing the same error when mocking a
post
request. -
Ariel almost 2 yearsYes, you've got to use http_mock_adapter: ^0.1.4 for mocking.
-