Skip to content

Commit b71406b

Browse files
committed
Create news remote data source
1 parent 69db696 commit b71406b

File tree

5 files changed

+159
-4
lines changed

5 files changed

+159
-4
lines changed

lib/config/base_url_config.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
class BaseUrlConfig {
2-
final String baseUrlDevelopment = 'https://bengkelrobot.net:8003/v2';
3-
final String baseUrlProduction = 'https://newsapi.org/v2';
2+
final String baseUrlDevelopment = 'https://bengkelrobot.net:8003';
3+
final String baseUrlProduction = 'https://newsapi.org';
44
}

lib/config/constant_config.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class ConstantConfig {
2+
final String apiKeyNewsApi = '3f1bdb0927c94f8da5708f7a36f7d06f';
3+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'package:dio/dio.dart';
2+
import 'package:flutter_news_app/config/constant_config.dart';
3+
import 'package:flutter_news_app/feature/data/model/topheadlinesnews/top_headlines_news_response_model.dart';
4+
import 'package:meta/meta.dart';
5+
6+
abstract class NewsRemoteDataSource {
7+
/// Calls the baseUrl:/v2/top-headlines?country=:country&apiKey=:apiKey endpoint
8+
///
9+
/// Throws a [DioError] for all error codes.
10+
Future<TopHeadlinesNewsResponseModel> getTopHeadlinesNews();
11+
}
12+
13+
class NewsRemoteDataSourceImpl implements NewsRemoteDataSource {
14+
final Dio dio;
15+
final ConstantConfig constantConfig;
16+
17+
NewsRemoteDataSourceImpl({
18+
@required this.dio,
19+
@required this.constantConfig,
20+
});
21+
22+
@override
23+
Future<TopHeadlinesNewsResponseModel> getTopHeadlinesNews() async {
24+
var response = await dio.get(
25+
'/v2/top-headlines',
26+
queryParameters: {
27+
'country': 'id',
28+
'apiKey': constantConfig.apiKeyNewsApi,
29+
},
30+
);
31+
if (response.statusCode == 200) {
32+
return TopHeadlinesNewsResponseModel.fromJson(response.data);
33+
} else {
34+
throw DioError();
35+
}
36+
}
37+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import 'dart:convert';
2+
3+
import 'package:dio/dio.dart';
4+
import 'package:flutter_news_app/config/base_url_config.dart';
5+
import 'package:flutter_news_app/config/constant_config.dart';
6+
import 'package:flutter_news_app/feature/data/datasource/news/news_remote_data_source.dart';
7+
import 'package:flutter_news_app/feature/data/model/topheadlinesnews/top_headlines_news_response_model.dart';
8+
import 'package:flutter_test/flutter_test.dart';
9+
import 'package:matcher/matcher.dart';
10+
import 'package:mockito/mockito.dart';
11+
12+
import '../../../../fixture/fixture_reader.dart';
13+
14+
class MockDioAdapter extends Mock implements HttpClientAdapter {}
15+
16+
class MockDio extends Mock implements Dio {}
17+
18+
void main() {
19+
NewsRemoteDataSourceImpl newsRemoteDataSource;
20+
ConstantConfig constantConfig;
21+
MockDio mockDio;
22+
MockDioAdapter mockDioAdapter;
23+
24+
setUp(() {
25+
mockDio = MockDio();
26+
constantConfig = ConstantConfig();
27+
mockDioAdapter = MockDioAdapter();
28+
mockDio.httpClientAdapter = mockDioAdapter;
29+
mockDio.options = BaseOptions(
30+
baseUrl: BaseUrlConfig().baseUrlDevelopment,
31+
);
32+
newsRemoteDataSource = NewsRemoteDataSourceImpl(
33+
dio: mockDio,
34+
constantConfig: constantConfig,
35+
);
36+
});
37+
38+
group('getTopHeadlinesNews', () {
39+
final tTopHeadlinesNewsResponseModel = TopHeadlinesNewsResponseModel.fromJson(
40+
json.decode(
41+
fixture('top_headlines_news_response_model.json'),
42+
),
43+
);
44+
45+
void setUpMockDioSuccess() {
46+
final responsePayload = json.decode(fixture('top_headlines_news_response_model.json'));
47+
final response = Response(
48+
data: responsePayload,
49+
statusCode: 200,
50+
headers: Headers.fromMap({
51+
Headers.contentTypeHeader: [Headers.jsonContentType],
52+
}),
53+
);
54+
when(
55+
mockDio.get(
56+
any,
57+
queryParameters: anyNamed('queryParameters'),
58+
),
59+
).thenAnswer((_) async => response);
60+
}
61+
62+
test(
63+
'make sure there is a GET request to endpoint /v2/top-headlines?country=:country&apiKey=:apiKey',
64+
() async {
65+
// arrange
66+
setUpMockDioSuccess();
67+
68+
// act
69+
await newsRemoteDataSource.getTopHeadlinesNews();
70+
71+
// assert
72+
verify(
73+
mockDio.get(
74+
'/v2/top-headlines',
75+
queryParameters: {
76+
'country': 'id',
77+
'apiKey': constantConfig.apiKeyNewsApi,
78+
},
79+
),
80+
);
81+
},
82+
);
83+
84+
test(
85+
'make sure to return the TopHeadlinesNewsResponseModel object when the '
86+
'response code is successful from the endpoint',
87+
() async {
88+
// arrange
89+
setUpMockDioSuccess();
90+
91+
// act
92+
final result = await newsRemoteDataSource.getTopHeadlinesNews();
93+
94+
// assert
95+
expect(result, tTopHeadlinesNewsResponseModel);
96+
},
97+
);
98+
99+
test(
100+
'make sure to receive DioError when the response code fails from the endpoint',
101+
() async {
102+
// arrange
103+
final response = Response(
104+
data: 'Bad Request',
105+
statusCode: 400,
106+
);
107+
when(mockDio.get(any, queryParameters: anyNamed('queryParameters'))).thenAnswer((_) async => response);
108+
109+
// act
110+
final call = newsRemoteDataSource.getTopHeadlinesNews();
111+
112+
// assert
113+
expect(() => call, throwsA(TypeMatcher<DioError>()));
114+
},
115+
);
116+
});
117+
}

test/fixture/fixture_reader.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import 'dart:io';
22

3-
final baseUrlTest = 'https://bengkelrobot.net:8003/v2';
4-
53
String fixture(String name) {
64
var currentDirectory = Directory.current.toString().replaceAll('\'', '');
75
var lastDirectory = currentDirectory.split('/')[currentDirectory.split('/').length - 1];

0 commit comments

Comments
 (0)