Go は Google によって開発されたオープンソースのプログラミング言語です。シンプルで信頼性の高い、効率的なソフトウェアを簡単に構築することができます。ネットワーク サーバーとの相性に優れ、構文もわかりやすいので、Node.js に代わる言語として有用です。どんなネットワーク アプリケーションでも、機能の十分なテストは不可欠であり、Go で開発されたアプリケーションも例外ではありません。このチュートリアルでは Go でシンプルなブログを構築し、さらにテストも行います。

前提条件

このチュートリアルを進めるには、いくつかの準備が必要です。

  1. プログラミングの基礎を理解する
  2. Go をローカル システムにインストールする (インストール ガイドはこちら)
  3. CircleCI アカウントを用意する
  4. GitHub アカウントを用意する

これらの前提条件の準備ができたら、チュートリアルを始めていきましょう。

シンプルな 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 プロパティを含んでおり、それぞれを jsontitlebody にバインドしています。
  • 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 に出力されるはずです。

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 (プロジェクトの追加)] ページにアクセスします。

プロジェクトをCircleCiに追加

[Set Up Project (プロジェクトをセットアップ)] をクリックします。

コンフィグを設定

[Set Up Project (プロジェクトをセットアップ)] ページが表示されたら、表示されたサンプルを無視して設定ファイルを手動で追加するため、[Use Existing Config (既存の設定ファイルを使用する)] をクリックします。パイプラインの設定ファイルをダウンロードするのか、ビルドを開始するのかを確認するメッセージが表示されます。

Build Prompt - CircleCI

[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) を実行します。

変更をプロジェクトにコミットしてからリモート リポジトリにプッシュし、スクリプトを実行しましょう。

ビルドの成功画面 サークルCI

[SUCCESS (成功)] ラベルをクリックすると、デプロイの詳細を確認できます。

ビルドやデプロイの詳細画面

うまく行きました!

まとめ

Go では、このチュートリアルで示したように、設定不要のテストスイートを利用できます。Go のメリットを最大限に活かしてワークロードのコストを削減すれば、テスト駆動開発 (TDD) がずっと容易になります。

すばらしいコーディングができますように!


Fikayo Adepoju は、Web とモバイル テクノロジー、DevOps に精通した LinkedIn Learning (Lynda.com) 講師、フルスタック開発者、テクニカル ライター、テクニカル コンテンツ クリエイターです。スケーラブルな分散アプリケーション開発については 10 年以上の経験を持っています。 CircleCI、Twilio、Auth0、The New Stack のブログで 40 以上の記事を執筆するほか、個人の Medium ページでも情報を発信しており、役立つ知識を多くの開発者に広めることに専心しています。 また、Udemy で動画形式のコース (英語) も開講しています。ぜひご覧ください。

さんの他の投稿を読む Fikayo Adepoju