[Go]テストコード

# 1. テストコードの実装に必要なこと
- テストコードを書くファイルについて
    - ファイル名: ``xxx_test.go``とする
    - ファイルの配置: テスト対象関数のパッケージのディレクトリ
- テストコードファイルの記述
    - ``package``ディレクティブでテスト対象関数のパッケージを指定
    - 標準出力のテストの場合
        - 対象関数に対するテストは、引数なしの``ExampleXxx``という名前の関数で記述する
    - 標準出力以外のテストの場合
        - ``import``ディレクティブでテスト``testing``パッケージを指定
        - 対象関数に対するテストは、引数が``t *testing.T``である``TestXxx``という名前の関数で記述する


# 2. テストコードの実装
## 2.1. 前提
以下のような構成・内容のモジュールが存在するとして、各関数に対してテストコードを実装する場合を例にする。


**ディレクトリ・ファイル構成** 
```
go_test_code/
  return_values/
    return_add_value.go
    return_sub_value.go
  print_values/
    print_add_value.go
    print_sub_value.go
  go.mod
```

```return_add_value.go
package return_values

// 引数x, yの和を返す
func ReturnAddValue(x, y int) int {
  return x + y
}
```

```return_sub_value.go
package return_values

// 引数x, yの差を返す
func ReturnSubValue(x, y int) int {
  return x - y
}
```

```print_add_value.go
package print_values

import "fmt"

// 引数x, yの和を出力
func PrintAddValue(x, y int) {
  fmt.Println(x + y)
}
```

```print_add_value.go
package print_values

import "fmt"

// 引数x, yの差を出力
func PrintSubValue(x, y int) {
  fmt.Println(x - y)
}
```

```go.mod
module github.com/<オーナー名>/go_test_code

go 1.19
```

※go.modは、「ディレクトリ・ファイル構成」にあるgo.mod以外のディレクトリ・ファイルを配置した上で``go mod init github.com/<オーナー名>/go_test_code``を実行して作成

## 2.2. テストコードの実装

2.1.に引き続いて、テストコードファイルとして、以下★のファイルを作成する。

**ディレクトリ・ファイル構成**  
```
go_test_code/
  return_values/
    return_add_value.go
    return_sub_value.go
    return_values_test.go # ★
  print_values/
    print_add_value.go
    print_sub_value.go
    print_values_test.go # ★
  go.mod
```

```return_values_test.go
package return_values

import "testing"

// テスト関数名はTestXxxという形でさえあればいいが、「Test+<テスト対象関数名>」という命名にするのが無難そう

// ReturnAddValue()のテスト
func TestReturnAddValue(t *testing.T) {
    // テストに際し、テスト対象関数に与える引数を定義
    x, y := 2, 3

    // 期待結果を定義
    expect := 5

    // テスト対象関数を実行
    got := ReturnAddValue(x, y)
    
    // 実行結果と期待結果を比較し、エラーだったら出力
    if got != expect {
      t.Errorf("exec ReturnAddValue() expect: %v, got: %v, x: %v, y: %v", expect, got, x, y)
    }
}

// ReturnSubValue()のテスト
func TestReturnSubValue(t *testing.T) {
	// テストに際し、テスト対象関数に与える引数を定義
	x, y := 8, 3

	// 期待結果を定義
	expect := 5

	// テスト対象関数を実行
	got := ReturnSubValue(x, y)
	
	// 実行結果と期待結果を比較し、エラーだったら出力
	if got != expect {
		t.Errorf("exec ReturnSubValue() expect: %v, got: %v, x: %v, y: %v", expect, got, x, y)
	}
}


```

```print_values_test.go
package print_values

// PrintAddValue()のテスト
func ExamplePrintAddValue() {
	// テストに際し、テスト対象関数に与える引数を定義
	x, y := 2, 3

	// テスト対象関数を実行
    PrintAddValue(x,y)

	// 期待結果

    // Output:
    // 5
}

// PrintSubValue()のテスト
func ExamplePrintSubValue() {
	// テストに際し、テスト対象関数に与える引数を定義
	x, y := 2, 3

	// テスト対象関数を実行
    PrintSubValue(x,y)

	// 期待結果
	
    // Output:
    // -1
}
```

# 2.3. テストの実行
```
# モジュールパス直下へ移動
cd <モジュールパス直下>

# テストの実行
## カレントディレクトリ以下のすべてのテストコードを実行する場合
go test -v ./...
## 特定のディレクトリのテストコードを実行する場合
go test -v <ディレクトリへのパス>
## 特定のディレクトリ以下のすべてのテストコードを実行する場合
go test -v <ディレクトリへのパス>/...
## カレントディレクトリ以下の特定の文字列をテスト関数名に含むテストコードを実行する場合
go test -v -run <文字列> ./...
``` 

**補足**  
``...``は、goコマンドでワイルドカード的な意味をもつらしい。
``-v``はとりあえずつけておいた方がよい。


# 3. ルールを破ったらどうなるのか実験
## 3.1. テストコードのファイル名を``_test.go``じゃないものにして実行  
「テストファイルがない」と言われる！
```
# go_test_code/print/values/return_values_test.go を return_values_test2.goに変更してある
go test -v ./print_values
=>
?       github.com/<オーナー名>/go_test_code/print_values  [no test files]
```

## 3.2. テスト関数名を``TestXxx``、じゃないものにして実行
テストが飛ばされる！(エラーは発生しない)

```
# 1. 何もせず以下実行
go test -v ./return_values
=>
=== RUN   TestReturnAddValue
--- PASS: TestReturnAddValue (0.00s)
=== RUN   TestReturnSubValue
--- PASS: TestReturnSubValue (0.00s)
PASS
ok      github.com/<オーナー名>/go_test_code/return_values

# 2. return_values_test.go内の「TestReturnAddValue」を「TesReturnAddValue」に変更後に以下実行
go test -v ./return_values
=> [TestReturnAddValueの出力がない！]
=== RUN   TestReturnSubValue
--- PASS: TestReturnSubValue (0.00s)
PASS
ok      github.com/<オーナー名>/go_test_code/return_values 0.230s
```

## 3.3. テスト対象関数のファイルがあるディレクトリじゃないところへ、テスト対象関数のテストコードを書いた``_test.go``ファイルを配置して実行
テスト対象の関数が未定義だと言われ、ビルドに失敗する

```
# go_test_code/return_values/return_values_test.go を　go_test_code/ へ移動して以下実行
go test -v ./...
=>
# github.com/<オーナー名>/go_test_code [github.com/<オーナー名>/go_test_code.test]
./return_values_test.go:16:12: undefined: ReturnAddValue
./return_values_test.go:33:9: undefined: ReturnSubValue
FAIL    github.com/<オーナー名>/go_test_code [build failed]
=== RUN   ExamplePrintAddValue
--- PASS: ExamplePrintAddValue (0.00s)
=== RUN   ExamplePrintSubValue
--- PASS: ExamplePrintSubValue (0.00s)
PASS
ok      github.com/<オーナー名>/go_test_code/print_values  (cached)
?       github.com/<オーナー名>/go_test_code/return_values [no test files]
FAIL
```

# 参考

https://qiita.com/saya713y/items/f7ee07e8f12ab85ed9bf

https://www.wakuwakubank.com/posts/866-go-test/

https://qiita.com/tmzkysk/items/8bb37795ac223664d682

https://www.web-dev-qa-db-ja.com/ja/go/go%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%97%E3%81%A73%E3%81%A4%E3%81%AE%E3%83%89%E3%83%83%E3%83%88%E3%80%8C%E3%80%82-%E3%80%8D%E3%81%AF%E4%BD%95%E3%82%92%E6%84%8F%E5%91%B3%E3%81%97%E3%81%BE%E3%81%99%E3%81%8B%EF%BC%9F/1051682939/

https://www.yoheim.net/blog.php?q=20170903

https://budougumi0617.github.io/2018/08/30/go-testing2018-examples/