【初歩の初歩】Go言語でテストコードを作ってみた

## はじめに
この記事は、エンジニア成り立ての「見習いエンジニア」が作成した記事になります。記事の内容で間違っているとことがありましたらコメントでご指摘いただけますと非常に励みになります！

## テストコード作成の決まり
### その１『ファイル名』
 Goのテストでは、ファイル名を以下のような記載にする必要があります。
 ```go
 xxx_test.go
 ```
 
### その２『"testing"』パッケージ
そして、importに以下のように"testing"パッケージの記載が必要です。
```go
import {
    "testing"
}
```

### その３『関数名は"func TestXxx(t *testing.T)"』
以上をまとめると以下のような記載になります。
```go
// ファイル名　xxx_test.go

packege main

import {
    "testing"
}

func TestXxx(t *testing.T) {
    テスト内容
}
```

:::note info
(t *testing.T)って何？

(t *testing.T) はGoのテスト関数における慣習的な引数です。

　*testing.T 型の変数で、Goの testing パッケージが提供する型。ここの変数 t はテスト中の状態を管理し、テストの制御やレポートを行うためのメソッドをいくつか提供できます。

変数 t を使用することにより、テスト関数の中で、以下のような操作が可能になります。
- **t.Errorf()**：エラーメッセージを出力してテストを失敗とします。
- **t.Fatalf()**：エラーメッセージを出力してテストを中止します。
- **t.Logf()**：テストのログ出力を行います。
:::

## テストコードの書いてみよう
### モデルの例(model)
```go
package model

type NameModel struct {
	FamilyName string
	LastName   string
}

func NewNameModel(
    familyName string, 
    lastName string) *NameModel {
    
	return &NameModel{
		FamilyName: familyName,
		LastName:   lastName,
	}
}
```

### テストの例（test） ~正しい結果.ver~
```go
package model

import "testing"

func TestNewNameModel(t *testing.T) {
	familyName := "山田"
	lastName   := "太郎"

	// NewNameModelを使って新しいNameModelを作成
	nm := NewNameModel(familyName, lastName)

	// FamilyNameが正しく設定されているかを確認
	if nm.FamilyName != familyName {
		t.Errorf("FamilyNameが期待する値でない。期待値: %v, 実際: %v", familyName, nm.FamilyName)
	}

	// LastNameが正しく設定されているかを確認
	if nm.LastName != lastName {
		t.Errorf("LastNameが期待する値ではない。期待値: %v, 実際: %v", lastName, nm.LastName)
	}
}

```

以上のテストコードをターミナルで以下のコマンドで実行してみます。
```go
$ go test
    
PASS
ok      namaModel       0.241s
```
このように ok　と出力されれば、テストは成功しています。

ちなみに、以下のコマンドの場合はログの詳細を含めてた上で出力がされます。
```go
$ go test -v //（verboseモード）詳細情報を出力するためのログ出力

=== RUN   TestNewNameModel
--- PASS: TestNewNameModel (0.00s)
PASS
ok      namaModel       0.225s
```

さらにちなみに、以下のコマンドにすると、指定のテスト関数のみが実行されます。
```go
$ go test -run TestNewNameModel -v

=== RUN   TestNewNameModel
--- PASS: TestNewNameModel (0.00s)
PASS
ok      namaModel       0.298s
```

### テストの例（test） ~エラー結果.ver~
```go
package model

import "testing"

func TestNewNameModel(t *testing.T) {
	familyName := "山田"
	lastName   := "太郎"

    // この部分で故意に間違った値を設定
	nm := NewNameModel("田中", "一郎") 

	if nm.FamilyName != familyName {
		t.Errorf("FamilyNameが期待する値でない。期待値: %v, 実際: %v", familyName, nm.FamilyName)
	}

	if nm.LastName != lastName {
		t.Errorf("LastNameが期待する値ではない。期待値: %v, 実際: %v", lastName, nm.LastName)
	}
}
```

このように正常系の結果と違う異常系の場合は以下のような結果になります。
```go
$ go test -v

=== RUN   TestNewNameModel
    name_test.go:15: FamilyNameが期待する値でない。期待値: 山田, 実際: 田中
    name_test.go:20: LastNameが期待する値ではない。期待値: 太郎, 実際: 一郎
--- FAIL: TestNewNameModel (0.00s)
FAIL
exit status 1
```
TestNewNameModel を実行（Run）したところ、
```
name_test.go:15: FamilyNameが期待する値でない。期待値: 山田, 実際: 田中
name_test.go:20: LastNameが期待する値ではない。期待値: 太郎, 実際: 一郎
```
という結果でテストが失敗しました。
この結果は、NameModel のテストが期待する値と一致しなかったことを示しています。

このような記載が一般的なGoのテスト方法になります。これをベースにより詳細にテストを作成することも可能です。

## t.Logf()関数を使用してみる
先ほどに記載しました**t.Logf()関数**を使用することで、テストの実行中にログメッセージを出力するために使用されます。

テストが成功した場合、これらのログメッセージは通常非表示ですが、テストが失敗した場合に出力されます。

### t.Logf()の例
```go
package model

import "testing"

func TestNewNameModel(t *testing.T) {
	// NewNameModelを使って新しいNameModelを作成
	nm := NewNameModel("山田", "太郎")

	// テストの開始をログ出力
	t.Log("NewNameModelのテストを開始します。")

	// FamilyNameが正しく設定されているかを確認
	if nm.FamilyName != "山田" {
		t.Errorf("FamilyNameが正しくありません。期待する値: %s, 取得した値: %s", "山田", nm.FamilyName)
	} else {
		t.Logf("FamilyNameが正しく設定されています。値: %s", nm.FamilyName)
	}

	// LastNameが正しく設定されているかを確認
	if nm.LastName != "太郎" {
		t.Errorf("LastNameが正しくありません。期待する値: %s, 取得した値: %s", "太郎", nm.LastName)
	} else {
		t.Logf("LastNameが正しく設定されています。値: %s", nm.LastName)
	}
}
```

```go
$ go test -run TestNewNameModel -v
=== RUN   TestNewNameModel
    name_test.go:10: NewNameModelのテストを開始します。
    name_test.go:16: FamilyNameが正しく設定されています。値: 山田
    name_test.go:23: LastNameが正しく設定されています。値: 太郎
--- PASS: TestNewNameModel (0.00s)
PASS
ok      namaModel       0.274s
```
このように`t.Logf`を使用すると、より詳細にテストコードの結果を出力することができます。

:::note info
**『%s』って何？**
`%s` はGo言語における書式指定子(フォーマット指定子)の一つです。
PrintfやSprintf、Fprintfなどの関数で使用され、文字列（String）を書式指定して出力する場合に使用します。
今回の場合は、`Logf`の関数において`nm変数`のFamilyNameプロパティの文字列値を出力しています。
:::

## まとめ
今回は、Go言語での基本的なテストコードの記載方法をまとめてみました。
今後は、テーブル駆動テストの記載方法をまとめていきたいと思います。

**まだまだ学べることに感謝！**

以上

## この記事は以下のサイトや書籍を参考にして執筆しました
https://note.com/rescuenow_hr/n/n9ed7caf4646d

https://qiita.com/Sekky0905/items/c9cbda2498a685517ad0#s-%E6%96%87%E5%AD%97%E5%88%97%E3%82%B9%E3%83%A9%E3%82%A4%E3%82%B9