Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion codemagic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ workflows:
events:
- push
branch_patterns:
- pattern: '*dev*'
- pattern: '*'
include: true
source: true
scripts:
Expand Down
4 changes: 2 additions & 2 deletions lib/config/base_url_config.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class BaseUrlConfig {
final String baseUrlDevelopment = 'https://bengkelrobot.net:8003/v2';
final String baseUrlProduction = 'https://newsapi.org/v2';
final String baseUrlDevelopment = 'https://bengkelrobot.net:8003';
final String baseUrlProduction = 'https://newsapi.org';
}
3 changes: 3 additions & 0 deletions lib/config/constant_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ConstantConfig {
final String apiKeyNewsApi = '3f1bdb0927c94f8da5708f7a36f7d06f';
}
37 changes: 37 additions & 0 deletions lib/feature/data/datasource/news/news_remote_data_source.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:dio/dio.dart';
import 'package:flutter_news_app/config/constant_config.dart';
import 'package:flutter_news_app/feature/data/model/topheadlinesnews/top_headlines_news_response_model.dart';
import 'package:meta/meta.dart';

abstract class NewsRemoteDataSource {
/// Calls the baseUrl:/v2/top-headlines?country=:country&apiKey=:apiKey endpoint
///
/// Throws a [DioError] for all error codes.
Future<TopHeadlinesNewsResponseModel> getTopHeadlinesNews();
}

class NewsRemoteDataSourceImpl implements NewsRemoteDataSource {
final Dio dio;
final ConstantConfig constantConfig;

NewsRemoteDataSourceImpl({
@required this.dio,
@required this.constantConfig,
});

@override
Future<TopHeadlinesNewsResponseModel> getTopHeadlinesNews() async {
var response = await dio.get(
'/v2/top-headlines',
queryParameters: {
'country': 'id',
'apiKey': constantConfig.apiKeyNewsApi,
},
);
if (response.statusCode == 200) {
return TopHeadlinesNewsResponseModel.fromJson(response.data);
} else {
throw DioError();
}
}
}
117 changes: 117 additions & 0 deletions test/feature/data/datasource/news/news_remote_data_source_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:flutter_news_app/config/base_url_config.dart';
import 'package:flutter_news_app/config/constant_config.dart';
import 'package:flutter_news_app/feature/data/datasource/news/news_remote_data_source.dart';
import 'package:flutter_news_app/feature/data/model/topheadlinesnews/top_headlines_news_response_model.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:matcher/matcher.dart';
import 'package:mockito/mockito.dart';

import '../../../../fixture/fixture_reader.dart';

class MockDioAdapter extends Mock implements HttpClientAdapter {}

class MockDio extends Mock implements Dio {}

void main() {
NewsRemoteDataSourceImpl newsRemoteDataSource;
ConstantConfig constantConfig;
MockDio mockDio;
MockDioAdapter mockDioAdapter;

setUp(() {
mockDio = MockDio();
constantConfig = ConstantConfig();
mockDioAdapter = MockDioAdapter();
mockDio.httpClientAdapter = mockDioAdapter;
mockDio.options = BaseOptions(
baseUrl: BaseUrlConfig().baseUrlDevelopment,
);
newsRemoteDataSource = NewsRemoteDataSourceImpl(
dio: mockDio,
constantConfig: constantConfig,
);
});

group('getTopHeadlinesNews', () {
final tTopHeadlinesNewsResponseModel = TopHeadlinesNewsResponseModel.fromJson(
json.decode(
fixture('top_headlines_news_response_model.json'),
),
);

void setUpMockDioSuccess() {
final responsePayload = json.decode(fixture('top_headlines_news_response_model.json'));
final response = Response(
data: responsePayload,
statusCode: 200,
headers: Headers.fromMap({
Headers.contentTypeHeader: [Headers.jsonContentType],
}),
);
when(
mockDio.get(
any,
queryParameters: anyNamed('queryParameters'),
),
).thenAnswer((_) async => response);
}

test(
'make sure there is a GET request to endpoint /v2/top-headlines?country=:country&apiKey=:apiKey',
() async {
// arrange
setUpMockDioSuccess();

// act
await newsRemoteDataSource.getTopHeadlinesNews();

// assert
verify(
mockDio.get(
'/v2/top-headlines',
queryParameters: {
'country': 'id',
'apiKey': constantConfig.apiKeyNewsApi,
},
),
);
},
);

test(
'make sure to return the TopHeadlinesNewsResponseModel object when the '
'response code is successful from the endpoint',
() async {
// arrange
setUpMockDioSuccess();

// act
final result = await newsRemoteDataSource.getTopHeadlinesNews();

// assert
expect(result, tTopHeadlinesNewsResponseModel);
},
);

test(
'make sure to receive DioError when the response code fails from the endpoint',
() async {
// arrange
final response = Response(
data: 'Bad Request',
statusCode: 400,
);
when(mockDio.get(any, queryParameters: anyNamed('queryParameters'))).thenAnswer((_) async => response);

// act
final call = newsRemoteDataSource.getTopHeadlinesNews();

// assert
expect(() => call, throwsA(TypeMatcher<DioError>()));
},
);
});
}
2 changes: 0 additions & 2 deletions test/fixture/fixture_reader.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import 'dart:io';

final baseUrlTest = 'https://bengkelrobot.net:8003/v2';

String fixture(String name) {
var currentDirectory = Directory.current.toString().replaceAll('\'', '');
var lastDirectory = currentDirectory.split('/')[currentDirectory.split('/').length - 1];
Expand Down