はじめに
前回までの続きです。今回はDomainServieでポケモンバトルを実装していきます。
ソースコード
ディレクトリ構成
├── domain
│ ├── entities
│ │ ├── move.dart
│ │ ├── pokemon.dart
│ │ └── stats.dart
│ ├── enums
│ │ └── pokemon_type.dart
│ ├── repositories
│ │ └── pokemon_repository.dart
│ └── services
│ └── battle_service.dart
lib/domain/services/battle_service.dart
import '../repositories/pokemon_repository.dart';
import '../entities/pokemon.dart';
import '../entities/move.dart';
class BattleService {
final PokemonRepository _repository;
// 名前付きパラメータを定義
BattleService({required PokemonRepository repository})
: _repository = repository;
Future<void> startBattle(String name1, String name2) async {
final pokemon1 = await _repository.getPokemon(name1);
final pokemon2 = await _repository.getPokemon(name2);
print('Battle started between ${pokemon1.name} and ${pokemon2.name}');
// バトルロジックをここに追加
}
void useMove(Pokemon attacker, Pokemon defender, Move move) {
print('${attacker.name} uses ${move.name} on ${defender.name}');
// ダメージ計算やその他のバトルロジックをここに追加
}
void calculateDamage(Pokemon attacker, Pokemon defender, Move move) {
// 簡単なダメージ計算ロジック
int damage = (move.power * attacker.stats.attack) ~/ defender.stats.defense;
print('${defender.name} receives $damage damage');
// ここで防御側のHPを減らすロジックを追加する
}
}
コンストラクタでインスタンスを作成。インスタンスに紐づくデータアクセスを実現している。
テストコードの実装
test/domain/services/battle_service_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
import 'package:pokemon_battle_app/domain/entities/move.dart';
import 'package:pokemon_battle_app/domain/entities/pokemon.dart';
import 'package:pokemon_battle_app/domain/entities/stats.dart';
import 'package:pokemon_battle_app/domain/enums/pokemon_type.dart';
import 'package:pokemon_battle_app/domain/repositories/pokemon_repository.dart';
import 'package:pokemon_battle_app/domain/services/battle_service.dart';
import 'battle_service_test.mocks.dart';
@GenerateMocks([PokemonRepository])
void main() {
group('BattleService', () {
final mockRepository = MockPokemonRepository();
final battleService = BattleService(repository: mockRepository);
final pikachu = Pokemon(
name: 'Pikachu',
imageUrl:
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png',
moves: [Move(name: 'Thunderbolt', power: 90)],
stats: Stats(hp: 35, attack: 55, defense: 40, speed: 90),
type: PokemonType.electric,
);
final charmander = Pokemon(
name: 'Charmander',
imageUrl:
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/4.png',
moves: [Move(name: 'Flamethrower', power: 90)],
stats: Stats(hp: 39, attack: 52, defense: 43, speed: 65),
type: PokemonType.fire,
);
setUp(() {
// setUp関数内でモックの設定を行う
when(mockRepository.getPokemon('Pikachu'))
.thenAnswer((_) async => pikachu);
when(mockRepository.getPokemon('Charmander'))
.thenAnswer((_) async => charmander);
});
test('should start a battle and print the correct message', () async {
await battleService.startBattle('Pikachu', 'Charmander');
verify(mockRepository.getPokemon('Pikachu')).called(1);
verify(mockRepository.getPokemon('Charmander')).called(1);
// コンソール出力の検証は通常のユニットテストでは行いませんが、
// 必要であれば `print` のモックを作成することもできます
});
test('should use a move and print the correct message', () {
battleService.useMove(pikachu, charmander, pikachu.moves.first);
// コンソール出力の検証は通常のユニットテストでは行いませんが、
// 必要であれば `print` のモックを作成することもできます
});
test('should calculate damage correctly', () {
battleService.calculateDamage(pikachu, charmander, pikachu.moves.first);
// コンソール出力の検証は通常のユニットテストでは行いませんが、
// 必要であれば `print` のモックを作成することもできます
});
});
}
今回はMockを利用しています。Mockはbuild_runner: ^2.1.11で作るので、事前にインストールしてください。
その後は、flutter pub run build_runner buildでMockが作成できます。