Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session/9 #22

Merged
merged 30 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5c89085
Add mockito package
Sep 19, 2023
e26a0bf
Add "WeatherData.fromJson" test
Sep 19, 2023
a6c27fd
Add weather_service_test.dart (WIP)
Sep 20, 2023
b0b3ff9
Added success case test
Sep 22, 2023
b5e3fbd
Add invalidParameter test case
Sep 26, 2023
6979131
Edit test: add group, edit test titles
Sep 26, 2023
eb8bf86
Add unknown test case
Sep 26, 2023
355e0c5
Fix grouping, test titles
Sep 27, 2023
eded9f2
Fix test based on session/8's changes
Sep 27, 2023
c8cd32b
update test grouping
Sep 28, 2023
2a45afb
Rename weather_data_test.dart -> weather_forecast_test.dart based on …
Sep 28, 2023
416107d
Add wrong key test case
Sep 28, 2023
abd3c72
Add unexpected type of value case
Sep 28, 2023
1c1bc2d
Add required property is missing case
Sep 28, 2023
3111065
Add json.decode test case
Sep 28, 2023
b1b743e
Add non-exist weather condition case (fromJson)
Sep 28, 2023
ae17ece
Add WeatherRequest encode test
Sep 28, 2023
0051ddf
Add WeatherStateNotifier success case
Sep 28, 2023
a8a8b97
Add weatherStateNotifierProvider.getWeather() failure case
Sep 28, 2023
d28e216
Add fromJson failure case
Oct 2, 2023
7928043
Add jsonDecode() failure case
Oct 2, 2023
e135276
Add fileNesting setting to settings.json
Oct 2, 2023
87ce0ab
json.decode -> jsonDecode
Oct 2, 2023
ca176b0
Fix: setUpAll -> setUp
Oct 3, 2023
32a6a97
Fix: delete final
Oct 3, 2023
faf0c56
Fix matcher form
Oct 4, 2023
8e9c979
Fix test description, rename a valuable
Oct 4, 2023
7fe16f5
Remove unnecessary jsonDecode test
Oct 4, 2023
427e790
Update group naming
Oct 6, 2023
c72000a
Fix test based on feedback
Oct 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@
},
"files.watcherExclude": {
"**/.fvm": true
},
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"*.dart": "$(capture).g.dart, $(capture).freezed.dart"
}
Yamasaki-pan961 marked this conversation as resolved.
Show resolved Hide resolved
}
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
mockito:
dependency: "direct main"
description:
name: mockito
sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616"
url: "https://pub.dev"
source: hosted
version: "5.4.2"
package_config:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies:

freezed_annotation: ^2.4.1
json_annotation: ^4.8.1
mockito: ^5.4.2
riverpod_annotation: ^2.1.5
yumemi_weather:
git:
Expand Down
107 changes: 107 additions & 0 deletions test/model/weather_forecast_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import 'dart:convert';

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_training/model/weather_condition.dart';
import 'package:flutter_training/model/weather_forecast.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

void main() {
test('success case: fromJson', () {
Yamasaki-pan961 marked this conversation as resolved.
Show resolved Hide resolved
const jsonData = '''
{
"weather_condition": "cloudy",
"max_temperature": 25,
"min_temperature": 7,
"date": "2023-09-19 10:24:31.877"
}
''';
final data = jsonDecode(jsonData) as Map<String, dynamic>;
final result = WeatherForecast.fromJson(data);

expect(
result,
WeatherForecast(
weatherCondition: WeatherCondition.cloudy,
maxTemperature: 25,
minTemperature: 7,
date: DateTime(2023, 9, 19, 10, 24, 31, 877),
),
);
});

group('fromJson failure: CheckedFromJsonException should be thrown.', () {
test('non-exist weather', () {
Yamasaki-pan961 marked this conversation as resolved.
Show resolved Hide resolved
const jsonData = '''
{
"weather_condition": "thunder",
"max_temperature": 25,
"min_temperature": 7,
"date": "2023-09-19 10:24:31.877"
}
''';

final data = jsonDecode(jsonData) as Map<String, dynamic>;

expect(
() => WeatherForecast.fromJson(data),
throwsA(isA<CheckedFromJsonException>()),
reason: 'thunder does not exist in WeatherCondition',
Yamasaki-pan961 marked this conversation as resolved.
Show resolved Hide resolved
);
});

test('jsonData has wrong key', () {
const jsonData = '''
{
"weather_condition_wrong_key": "cloudy",
"max_temperature": 25,
"min_temperature": 7,
"date": "2023-09-19 10:24:31.877"
}
''';

final data = jsonDecode(jsonData) as Map<String, dynamic>;

expect(
() => WeatherForecast.fromJson(data),
throwsA(isA<CheckedFromJsonException>()),
);
});

test('unexpected type of value', () {
const jsonData = '''
{
"weather_condition": 42.195,
"max_temperature": 25,
"min_temperature": 7,
"date": "2023-09-19 10:24:31.877"
}
''';

final data = jsonDecode(jsonData) as Map<String, dynamic>;

expect(
() => WeatherForecast.fromJson(data),
throwsA(isA<CheckedFromJsonException>()),
reason: 'weather_condition expect <WeatherCondition>',
);
});

test('required property is missing', () {
const jsonData = '''
{
"weather_condition": "cloudy",
"max_temperature": 25,
"date": "2023-09-19 10:24:31.877"
}
''';

final data = jsonDecode(jsonData) as Map<String, dynamic>;

expect(
() => WeatherForecast.fromJson(data),
throwsA(isA<CheckedFromJsonException>()),
reason: 'min_temperature is required',
);
});
});
}
20 changes: 20 additions & 0 deletions test/model/weather_reqest_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'dart:convert';

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_training/model/weather_request.dart';

void main() {
test('success case: encode WeatherRequest', () {
final request = WeatherRequest(
area: 'Nagoya',
date: DateTime(2023, 9, 19),
);

final requestJson = jsonEncode(request);
Yamasaki-pan961 marked this conversation as resolved.
Show resolved Hide resolved

expect(
requestJson,
'{"area":"Nagoya","date":"2023-09-19T00:00:00.000"}',
);
});
}
98 changes: 98 additions & 0 deletions test/service/weather_service_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_training/model/weather_condition.dart';
import 'package:flutter_training/model/weather_forecast.dart';
import 'package:flutter_training/model/weather_request.dart';
import 'package:flutter_training/service/weather_service.dart';
import 'package:flutter_training/utils/api/result.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:yumemi_weather/yumemi_weather.dart';

import 'weather_service_test.mocks.dart';

const jsonData = '''
{
"weather_condition": "cloudy",
"max_temperature": 25,
"min_temperature": 7,
"date": "2023-09-19T00:00:00.000"
}
''';

final request = WeatherRequest(
area: 'Nagoya',
date: DateTime(2023, 9, 19),
);

@GenerateNiceMocks([MockSpec<YumemiWeather>()])
void main() {
final mockClient = MockYumemiWeather();
late ProviderContainer container;

setUp(() {
//mockのYumemiWeatherでDIする
container = ProviderContainer(
overrides: [yumemiWeatherClientProvider.overrideWithValue(mockClient)],
);
});

tearDown(() {
container.dispose();
});

test('success case', () {
when(mockClient.fetchWeather(any)).thenReturn(jsonData);

final result = container.read(weatherServiceProvider).fetchWeather(request);

expect(
result,
isA<Success<WeatherForecast, String>>().having(
(success) => success.value,
'success weather data',
WeatherForecast(
weatherCondition: WeatherCondition.cloudy,
maxTemperature: 25,
minTemperature: 7,
date: DateTime(2023, 9, 19),
),
),
);
});

group('failure case', () {
test('invalidParameter error is thrown', () {
when(mockClient.fetchWeather(any))
.thenThrow(YumemiWeatherError.invalidParameter);

final result =
container.read(weatherServiceProvider).fetchWeather(request);

expect(
result,
isA<Failure<WeatherForecast, String>>().having(
(error) => error.exception,
'error message',
'パラメータが有効ではありません。',
),
);
});

test('unknown error is thrown', () {
when(mockClient.fetchWeather(any)).thenThrow(YumemiWeatherError.unknown);

final result =
container.read(weatherServiceProvider).fetchWeather(request);

expect(
result,
isA<Failure<WeatherForecast, String>>().having(
(error) => error.exception,
'error message',
'予期せぬエラーが発生しました。',
),
);
});
});
}
63 changes: 63 additions & 0 deletions test/service/weather_service_test.mocks.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Mocks generated by Mockito 5.4.2 from annotations
// in flutter_training/test/service/weather_service_test.dart.
// Do not manually edit this file.

// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:mockito/mockito.dart' as _i1;
import 'package:yumemi_weather/src/yumemi_weather_base.dart' as _i2;

// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class

/// A class which mocks [YumemiWeather].
///
/// See the documentation for Mockito's code generation for more information.
class MockYumemiWeather extends _i1.Mock implements _i2.YumemiWeather {
@override
String fetchSimpleWeather() => (super.noSuchMethod(
Invocation.method(
#fetchSimpleWeather,
[],
),
returnValue: '',
returnValueForMissingStub: '',
) as String);

@override
String fetchThrowsWeather(String? area) => (super.noSuchMethod(
Invocation.method(
#fetchThrowsWeather,
[area],
),
returnValue: '',
returnValueForMissingStub: '',
) as String);

@override
String fetchWeather(String? jsonString) => (super.noSuchMethod(
Invocation.method(
#fetchWeather,
[jsonString],
),
returnValue: '',
returnValueForMissingStub: '',
) as String);

@override
String syncFetchWeather(String? jsonString) => (super.noSuchMethod(
Invocation.method(
#syncFetchWeather,
[jsonString],
),
returnValue: '',
returnValueForMissingStub: '',
) as String);
}
Loading