Go は Google によって開発されたオープンソースのプログラミング言語です。シンプルで信頼性の高い、効率的なソフトウェアを簡単に構築することができます。ネットワーク サーバーとの相性に優れ、構文もわかりやすいので、Node.js に代わる言語として有用です。どんなネットワーク アプリケーションでも、機能の十分なテストは不可欠であり、Go で開発されたアプリケーションも例外ではありません。このチュートリアルでは Go でシンプルなブログを構築し、さらにテストも行います。
前提条件
このチュートリアルを進めるには、いくつかの準備が必要です。
これらの前提条件の準備ができたら、チュートリアルを始めていきましょう。
シンプルな Go ブログ プロジェクトを用意する
Go プロジェクトの準備として、プロジェクト フォルダーを作成し、そのフォルダーのルートに移動します。
mkdir go-testing
cd go-testing
ルートで go mod
コマンドを使用して、プロジェクトを初期化します。
go mod init go-testing
これで、プロジェクトが go-testing
というパッケージ名で初期化されます。パッケージ名は、標準のパッケージ命名規則に準拠している限り、どのようなものでもかまいません。
次に、アプリケーションのエントリ ポイントとなる main.go
ファイルを作成します。
package main
import "fmt"
func main(){
fmt.Println("Good to Go!")
}
上に紹介したコードは、Good to Go!
という文字列を CLI インターフェイスに出力します。CLI 上で次のコマンドを実行して、このコードを実行します。
go run main.go
ブログ モデルを作成する
この手順では、架空のブログ用のモデルを作成します (ここで言うモデルとは、オブジェクト指向プログラミングにおけるモデルを指します)。このモデルでは、新しい Blog
というインスタンスを作成して、そこへ複数の記事を追加します。
プロジェクトのルートで、新しい Go
ファイル blog.go
を作成し、以下のコードをペーストします。
package main
type Article struct {
Title string `json:"title"`
Body string `json:"body"`
}
type Blog struct {
Articles []Article
}
func New() *Blog {
return &Blog{}
}
func (b *Blog) SaveArticle(article Article) {
b.Articles = append(b.Articles, article)
}
func (b *Blog) FetchAll() []Article {
return b.Articles
}
このコードには 2 つのデータ型と 3 つの関数が含まれています。それぞれの説明は次のとおりです。
Article
: ブログ内の記事を定義するstruct
型です。Title
プロパティとBody
プロパティを含んでおり、それぞれをjson
のtitle
とbody
にバインドしています。Blog
: ブログ自体を表すstruct
型です。Article
の配列で構成されています。New()
: 新しいBlog
をインスタンス化するためのメソッドです。新しいBlog
インスタンスを返します。SaveArticle
: ブログの記事コレクションに新しい記事を追加するためのメソッドです。FetchAll
:Blog
インスタンスのすべての記事を取得するためのメソッドです。Article
の配列を返します。
作成したブログ モデルをテストするため、main.go
の内容を以下に置き換えます。
package main
import (
"fmt"
)
func main() {
blog := New()
fmt.Println(blog)
blog.SaveArticle(Article{"My first Blog post", "Today, we will be talking about blogging"})
fmt.Println(blog)
}
このコードでは、まずブログのインスタンスを新しく作成し、stdout
に出力します。次に SaveArticle
メソッドで新しい記事のインスタンスを保存してから、ブログを再び stdout
に出力します。今回はプロジェクトに Go
ファイルが複数存在するので、次のコマンドを使用してこれら 2 つのファイルを実行します。
go run blog.go main.go
以下の内容が CLI に出力されるはずです。
1 つ目の blog
インスタンスの出力は空です。2 つ目の出力は記事の追加後に実行されたものなので、その記事が含まれています。
ブログ プロジェクトにテストを追加する
それでは、テストを追加しましょう。SaveArticle
メソッドと FetchAll
メソッドが期待どおりに機能することを確認するためのテスト スクリプトを追加します。
プロジェクトのルートでテスト ファイル blog_test.go
を作成し、下のコードをペーストします。
package main
import "testing"
func TestSaveArticle(t *testing.T) {
blog := New()
blog.SaveArticle(Article{"My title", "My Post Body"})
if blog.Articles[0].Title != "My title" {
t.Errorf("Item was not added")
}
}
func TestFetchAllArticles(t *testing.T) {
blog := New()
blog.SaveArticle(Article{"My title", "My Post Body"})
articles := blog.FetchAll()
if len(articles) == 0 {
t.Errorf("Fetch All fails")
}
}
このコードでは、まず testing
パッケージ (Go
の標準ライブラリに付属) をインポートしています。その後、SaveArticle
メソッドと FetchAll
メソッドをテストする 2 つの関数を記述しています。
TestSaveArticle
では新しい Blog
インスタンスを作成して、1 つの Article
を保存します。その後、タイトルを確認して、保存した記事がブログに含まれているかどうかを調べます。保存した記事が存在しなかった場合、エラーが発生してテストは失敗します。
TestFetchAllArticles
では新しい Blog
インスタンスを作成して、1 つの Article
を保存します。その後、FetchAll
を呼び出して記事を取得し、ブログに記事が存在するかどうかの確認を行います。記事が存在しなかった場合、FetchAll
で新しい記事を返せなかったことを示すエラーが発生します。
ローカルでテストを実行する
詳しいテスト結果を得るために、verbose フラグ -v
を付けて上記のテストを実行してみましょう。以下のように入力してください。
go test -v
コマンドの実行が完了すると、CLI で以下のメッセージが返されるはずです。
このスクリーンショットでは、2 つのテストが正常に実行され、どちらの結果も合格であることが示されています。-v
フラグを追加したことで、デフォルトの結果概要ではなく、それぞれのテストの実行結果が詳しく示されました。
CircleCI でテストを自動化する
次に、テストプロセスを自動化して、コードがリモート リポジトリにプッシュされたらテストが実行されるようにします。CircleCI を使用してテストを自動化するには、3 つの手順を実施します。
- リモート リポジトリを作成し、プロジェクト コードをプッシュする
- そのリポジトリを CircleCI にプロジェクトとして追加する
- テストプロセスを自動化した CircleCI パイプラインの設定ファイルを追加する
まずは、プロジェクトを GitHub にプッシュしてください。
次に、CircleCI ダッシュボードの [Add Projects (プロジェクトの追加)] ページにアクセスします。
[Set Up Project (プロジェクトをセットアップ)] をクリックします。
[Set Up Project (プロジェクトをセットアップ)] ページが表示されたら、表示されたサンプルを無視して設定ファイルを手動で追加するため、[Use Existing Config (既存の設定ファイルを使用する)] をクリックします。パイプラインの設定ファイルをダウンロードするのか、ビルドを開始するのかを確認するメッセージが表示されます。
[Start Building (ビルドの開始)] をクリックします。設定ファイルを追加していないので、このビルドは失敗します。設定ファイルを手動で追加しましょう。
最後の手順として、ビルド パイプライン用設定ファイルを作成し、設定スクリプトを記述します。
プロジェクトのルートに .circleci
という名前のフォルダーを作成し、その中に config.yml
というファイルを作成します。config.yml
に以下の設定を入力します。
version: 2.1
jobs:
build:
working_directory: ~/repo
docker:
- image: circleci/golang:1.14.6
steps:
- checkout
- restore_cache:
keys:
- go-mod-v4-{{ checksum "go.sum" }}
- run:
name: Install Dependencies
command: go get ./...
- save_cache:
key: go-mod-v4-{{ checksum "go.sum" }}
paths:
- "/go/pkg/mod"
- run:
name: Run tests
command: go test -v
上記のスクリプトでは、まずテスト環境として Go
用の CircleCI Docker イメージをプルします。次に、プロジェクト コードをプロジェクトのリモート リポジトリからチェックアウトします。そして、依存関係のインストールとキャッシュ処理を完了してから、テスト スクリプト (go test -v
) を実行します。
変更をプロジェクトにコミットしてからリモート リポジトリにプッシュし、スクリプトを実行しましょう。
[SUCCESS (成功)] ラベルをクリックすると、デプロイの詳細を確認できます。
うまく行きました!
まとめ
Go
では、このチュートリアルで示したように、設定不要のテストスイートを利用できます。Go
のメリットを最大限に活かしてワークロードのコストを削減すれば、テスト駆動開発 (TDD) がずっと容易になります。
すばらしいコーディングができますように!