问题
I am trying to test a simple repository class which make a network call using the Dio package that is a dependency injection. Requirement of the Http.post is to send a Map object to a URL with the headers of 'Content-Type': 'application/json
. You can see this below:
class AuthenticateUserRemoteDataSourceImpl
implements AuthenticateUserRepository {
final Dio httpClient;
AuthenticateUserRemoteDataSourceImpl({@required this.httpClient});
@override
Future<Either<Failure, AuthenticateUser>> getAuthenticateUser(
String email, String password) async {
final url = 'API_URL';
final Map<String, String> jsonPayload = {
"email": email,
"password": password
};
Response response = await httpClient.post('{$url}api/users/authenticate',
data: jsonPayload,
options: Options(headers: {'Content-Type': 'application/json'}));
}
}
I am trying to make sure this method is covered by unit test, however I am having difficult verify the named parameters with the Dio package. I can verify that the dioClient.post
is actually called however, I am having trouble with mocking the name parameters of 'data' and 'options'.
I want to test that it gets called with Map<String, String>
for the body, and also I would like to test the headers that are sent, I want to check that is it called with the {'Content-Type': 'application/json'}
. This will be useful as well when I need to unit test Authenticated dio get/post/put requests.
This is the test that I have so far, which as mentioned before I can verify that the mocked function is called, but not verifying the name params.
group('getAuthenticateUser', () {
final tResponse = Response(statusCode: 200, data: {"title": "success"});
test(
'should perform a post request to the the API with the application/json header',
() async {
// arrange
when(dioClient.post(any,
data: anyNamed('data'), options: anyNamed('options')))
.thenAnswer((Invocation invocation) async => tResponse);
// act
await dataSource.getAuthenticateUser(tEmail, tPassword);
// assert
verify(dioClient.post(any,
data: anyNamed('data'), options: anyNamed('options')));
});
});
Thanks for your helps, I am just getting started with unit testing so any help or pointers will be great,
Thanks, Sam
@LoVe
Update
I have implemented your mock class which worked great, and I think it was the thing that I was missing for sure. I have updated my test but cannot understand where I am going wrong now?
test.dart
class MockOptions extends Mock implements Options {
final Map<String, dynamic> headers;
//also add any other parameters you want to mock as fields in this class
MockOptions({this.headers});
}
test(
'should perform a post request to the the API with the application/json header',
() async {
// arrange
Map<String, String> headers = {'Content-type': 'application/json'};
when(mockDio.post('path', options: anyNamed('options')))
.thenAnswer((_) async => Response(data: {}));
// act
dataSource.getAuthenticateUser(tEmail, tPassword);
// assert
verify(mockDio.post('path', options: MockOptions(headers: headers)));
});
and the method file looking like this:
@override
Future<Either<Failure, AuthenticateUser>> getAuthenticateUser(
String email, String password) async {
await this.httpClient.post('path',
data: {},
options: Options(headers: {'Content-type': 'application/json'}));
}
回答1:
Use http_mock_adapter, new 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
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:http_mock_adapter/http_mock_adapter.dart';
import 'package:flutter_test/flutter_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
final dio = Dio();
final dioAdapter = DioAdapter();
dio.httpClientAdapter = dioAdapter;
const path = 'https://example.com';
test('Expects Dioadapter to mock the data', () async {
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 postResposne = await dio.post(path);
expect(jsonEncode({'message': 'Successfully mocked POST!'}),
postResposne.data);
});
});
}
You can also use, DioInterceptor
of http_mock_adapter, which can be added to dio.interceptor
list.
view second example in examples file of http_mock_adapter
回答2:
to test that this method
Response response = await httpClient.post('{$url}api/users/authenticate',
data: jsonPayload,
options: Options(headers: {'Content-Type': 'application/json'}));
is called with proper arguments you should create an MockOption
class and pass an instance of it to the function call in your test
also you should create an object of type Map<String,dynamic>
(or whatever types the json uses) and pass that object also to the same method call to the data
parameter
then you use Mockito
's methods to verify that your method was called with the test arguments you have passed
and also to test the headers you do as I said here regarding the map:
also you should create an object of type
Map<String,dynamic>
(or whatever types the json uses) and pass that object also to the same method call
Please have a look at my answer here
Update:
so for example to mock the options
parameter you can do:
class MockOptions extends Mock implements Options{
final Map<String,dynamic> headers;
//also add any other parameters you want to mock as fields in this class
MockOptions(this.headers);
}
then, in your test, you do:
final MockOptions mockOptions = MockOptions({
//mocked map data here
});
来源:https://stackoverflow.com/questions/61321828/unit-test-how-to-mock-parameters-of-third-party-library-class-dio-in-flutter