ヘキサゴナルアーキテクチャでポケモンバトルを実施する。(Step2: coreのテスト実装)

## はじめに
[前回の記事](https://qiita.com/koji0705/items/65246ce4dc0b12501216)の続きです。


## テストコード
プロダクトコードに乱数を含むケースがあり、テストが安定しないため、プロダクトコードも一部修正しています。

```go:internal/core/move.go
package core

import (
	"math/rand"
	"time"
)

type Move struct {
	Name             string
	Power            int            // 技の威力
	Accuracy         int            // 命中率（0-100%）
	Type             string         // 技の属性（例: 電気、炎など）
	randSource       *rand.Rand     // 乱数生成器を保持
	randomFactorFunc func() float64 // ランダムなダメージ倍率を計算する関数
	hitCheckFunc     func() bool    // 命中判定の関数
}

// NewMove creates a new move and initializes the random source.
func NewMove(name string, power, accuracy int, moveType string) *Move {
	randSource := rand.New(rand.NewSource(time.Now().UnixNano()))
	move := &Move{
		Name:       name,
		Power:      power,
		Accuracy:   accuracy,
		Type:       moveType,
		randSource: randSource,
		randomFactorFunc: func() float64 {
			// デフォルトのダメージ倍率：0.85～1.0
			return 0.85 + randSource.Float64()*(1.0-0.85)
		},
		hitCheckFunc: func() bool {
			// デフォルトの命中判定：Accuracyに基づくランダムな命中判定
			return randSource.Intn(100) < accuracy
		},
	}
	return move
}

// CanHit checks if the move hits based on its accuracy.
func (m *Move) CanHit() bool {
	return m.hitCheckFunc() // 注入された命中判定関数を使用
}

// CalculateDamage calculates the damage of the move.
func (m *Move) CalculateDamage(attacker *Pokemon, defender *Pokemon) int {
	// ダメージ計算式
	damage := (attacker.Attack * m.Power) / defender.Defense

	// ランダム要素を使用してダメージを計算
	randomFactor := m.randomFactorFunc() // 注入された関数を使用
	damage = int(float64(damage) * randomFactor)

	if damage < 0 {
		damage = 0
	}
	return damage
}
```



```go:internal/core/pokemon_test.go
package core

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

// ポケモンのHPが正しく減少するかをテスト
func TestPokemonAttack_FixedValues(t *testing.T) {
	// Pikachu と Charmander のセットアップ
	pikachu := &Pokemon{
		Name:    "Pikachu",
		HP:      100,
		Attack:  55,
		Defense: 40,
		Speed:   90,
		Moves: []Move{
			*NewMove("Thunderbolt", 90, 95, "Electric"),
		},
	}

	charmander := &Pokemon{
		Name:    "Charmander",
		HP:      100,
		Attack:  52,
		Defense: 43,
		Speed:   65,
		Moves: []Move{
			*NewMove("Flamethrower", 90, 95, "Fire"),
		},
	}

	// Pikachu の技を取得
	move := &pikachu.Moves[0]

	// 乱数要素と命中判定を固定化する
	move.randomFactorFunc = func() float64 {
		return 1.0 // ダメージのランダム倍率を固定（1.0倍）
	}
	move.hitCheckFunc = func() bool {
		return true // 命中判定を固定（常に命中）
	}

	// Pikachu が Charmander を攻撃
	damage, err := pikachu.AttackPokemon(charmander, *move)

	// 結果を表示
	fmt.Println(pikachu.Name, "技：", move.Name, ", ", damage, "ダメージ！")

	// エラーがないことを確認
	assert.NoError(t, err)

	// ダメージが期待通りか確認
	expectedDamage := 115 // 期待する固定ダメージ（例: 計算に基づく値）
	assert.Equal(t, expectedDamage, damage, "ダメージが期待値と異なります")

	// Charmander のHPが正しく減少しているか確認
	expectedHP := 0
	assert.Equal(t, expectedHP, charmander.HP, "CharmanderのHPが期待値と異なります")
}

func TestCanHitWithFixedHitCheck(t *testing.T) {
	// テスト対象の技を作成
	move := NewMove("Thunderbolt", 90, 95, "Electric")

	// 命中判定を固定化（常に命中する）
	move.hitCheckFunc = func() bool {
		return true // 常に命中する
	}

	// CanHitの結果をテスト（命中するはず）
	if !move.CanHit() {
		t.Errorf("命中するはずのCanHitがfalseを返しました")
	}

	// 命中判定を固定化（常にミスする）
	move.hitCheckFunc = func() bool {
		return false // 常にミスする
	}

	// CanHitの結果をテスト（ミスするはず）
	if move.CanHit() {
		t.Errorf("ミスするはずのCanHitがtrueを返しました")
	}
}
```