go言語　単体テストをする

# 1.はじめに

Go言語で単体テストを実施したい
GO言語に標準で含まれている"testing"パッケージを利用してテストコードを書いてみる

以前投稿した以下の記事で作成したGoアプリにテストコードを追加する
[Qiitaの最新記事を教えてくれるアレクサスキルを作ろう　Alexa X Lambda X Golang　](https://qiita.com/keny-N/items/47b8dd299e65b6ca85bb#:~:text=github%E3%81%AB%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%8C%99%E3%81%92%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%A7%E5%8F%82%E8%80%83%E3%81%AB%E3%81%97%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84)

参考

https://pkg.go.dev/testing

# 2.単体テスト

## 2.1 テストファイルを用意する
テストしたいファイルと同じ階層にテストファイルを作成する。
テストファイルの名前は、`元のファイル名 + _test.go`にします。そうすることでtestファイルがbuild時に無視されるのでこの命名規則に合わせてテストファイルを作る

```
# ファイル構造 

└─NaviQiita
    │
    ├─cmd
    │  └─naviQiita
    │          main.go
    │          main_test.go # 新規作成
    │
    ├─internal
    │  └─qiita
    │          qiita.go
    |          qiita_test.go # 新規作成
    │
    └─pkg
        └─response
                response.go
                response_test.go # 新規作成
                
```

## 2.2 テストケースの記述

### 2.2.1 main.go

テストコードに記述する関数名は`TestXxx`にする
引数はtesting.T型にする必要がある
(例)`func TestXxx(*testing.T)`

```main.go
package main

import (
	"context"

	"githun.com/keny-N/NaviQiita/internal/qiita"
	"githun.com/keny-N/NaviQiita/pkg/response"

	"github.com/aws/aws-lambda-go/lambda"
)

func Handler(ctx context.Context) (response.AlexaResponse, error) {
	items, err := qiita.FetchQiitaItems()
	if err != nil {
		return response.AlexaResponse{}, err
	}

	message := "最新のQiitaの記事を５つ紹介します。。: "
	for _, item := range items {
		message += item.Title + ", URL: " + item.URL + ". "
	}

	return response.CreateResponse(message), nil
}

func main() {
	lambda.Start(Handler)
}

```

```main_test.go
package main

import (
	"context"
	"strings"
	"testing"
)

func TestHandler(t *testing.T) {
	ctx := context.Background()

	resp, err := Handler(ctx)
	if err != nil {
		t.Fatalf("Handler returned an error: %v", err)
	}

	// レスポンスの検証（特定のメッセージが含まれていることを確認）
	expectedMessageSubstring := "最新のQiitaの記事を５つ紹介します。"
	if !contains(resp.Response.OutputSpeech.Text, expectedMessageSubstring) {
		t.Errorf("Expected response to contain '%s', got '%s'", expectedMessageSubstring, resp.Response.OutputSpeech.Text)
	}
}

func contains(text, substring string) bool {
	return strings.Contains(text, substring)
}
```

### 2.2.2qiita.go
５件のアイテムを返すことを検証する

```qiita.go
package qiita

import (
	"encoding/json"
	"net/http"
)

type QiitaItem struct {
	Title string `json:"title"`
	URL   string `json:"url"`
}

func FetchQiitaItems() ([]QiitaItem, error) {
	resp, err := http.Get("https://qiita.com/api/v2/items?page=1&per_page=5")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var items []QiitaItem
	if err := json.NewDecoder(resp.Body).Decode(&items); err != nil {
		return nil, err
	}

	return items, nil
}

```

```qiita_test.go
package qiita

import (
	"testing"
)

func TestFetchQiitaItems(t *testing.T) {

	items, err := FetchQiitaItems()
	if err != nil {
		t.Fatalf("FetchQiitaItems() error = %v", err)
	}

	if len(items) != 5 {
		t.Errorf("Expected 2 items, got %d", len(items))
	}
}
```

### 2.2.3 response.go
レスポンスのバージョンや、タイプ、メッセージの検証を行う
```response.go
package response

type AlexaResponse struct {
	Version           string   `json:"version"`
	SessionAttributes struct{} `json:"sessionAttributes"`
	Response          struct {
		OutputSpeech struct {
			Type string `json:"type"`
			Text string `json:"text"`
		} `json:"outputSpeech"`
	} `json:"response"`
}

func CreateResponse(message string) AlexaResponse {
	return AlexaResponse{
		Version: "1.0",
		Response: struct {
			OutputSpeech struct {
				Type string `json:"type"`
				Text string `json:"text"`
			} `json:"outputSpeech"`
		}{
			OutputSpeech: struct {
				Type string `json:"type"`
				Text string `json:"text"`
			}{
				Type: "PlainText",
				Text: message,
			},
		},
	}
}

```

```response_test.go
package response

import (
	"testing"
)

func TestCreateResponse(t *testing.T) {

	message := "テストメッセージ"
	expectedType := "PlainText"

	result := CreateResponse(message)

	if result.Version != "1.0" {
		t.Errorf("Expected Version to be '1.0', got '%s'", result.Version)
	}
	if result.Response.OutputSpeech.Type != expectedType {
		t.Errorf("Expected OutputSpeech.Type to be '%s', got '%s'", expectedType, result.Response.OutputSpeech.Type)
	}
	if result.Response.OutputSpeech.Text != message {
		t.Errorf("Expected OutputSpeech.Text to be '%s', got '%s'", message, result.Response.OutputSpeech.Text)
	}
}

```

## 2.3 テストの実行
`go test コマンドで実行できる
```
go test ./...
ok      githun.com/keny-N/NaviQiita/cmd/naviQiita       0.785s
ok      githun.com/keny-N/NaviQiita/internal/qiita      0.753s
ok      githun.com/keny-N/NaviQiita/pkg/response        0.100s
```
`-v`をつけると詳細を確認できる
```
$ go test ./... -v
=== RUN   TestHandler
--- PASS: TestHandler (0.21s)
PASS
ok      githun.com/keny-N/NaviQiita/cmd/naviQiita       0.414s
=== RUN   TestFetchQiitaItems
--- PASS: TestFetchQiitaItems (0.24s)
PASS
ok      githun.com/keny-N/NaviQiita/internal/qiita      0.406s
=== RUN   TestCreateResponse
--- PASS: TestCreateResponse (0.00s)
PASS
ok      githun.com/keny-N/NaviQiita/pkg/response        0.104s
```

# 3.まとめ

簡単に単体テストを実行できる方法を試してみました。
gomockをつかってテストをする方法などもあるのでプライベートでも今後動かしてみたいと思います。


参考
https://github.com/golang/mock