ローカルでの Go の単体テスト

単体テストは、リモート コンポーネントにアクセスしない環境内で実行します。App Engine には、Cloud Datastore や他の App Engine サービスのローカル実装を使用するテスト ユーティリティが用意されています。

開発サービスは、実際のサービスの動作をテスト目的でローカルにシミュレートします。たとえば、Cloud Datastore と Memcache のテストを作成するで示されているデータストアの使用方法では、実際のデータベースに一切リクエストを行うことなく、データストア コードをテストできます。データストアの単体テストで生成されるエンティティはローカルに格納され、テストの実行後に削除されます。データストア自体に依存することはないため、簡単なテストをすばやく実行できます。

このドキュメントでは、Go の testing パッケージを使用して、ローカルの App Engine サービスに対する単体テストを作成する方法を説明します。

Go の testing パッケージ

Go パッケージのダウンロード、ビルド、テストは、goapp ツールを使用することで自動化できます。goapp は、App Engine SDK for Go に含まれています。

goapp test コマンドと標準の Go testing パッケージを組み合わせて、アプリケーション コードに対する単体テストの実行に使用できます。Go でのテストの背景情報については、How to Write Go Code の「Testing」セクションと testing パッケージ リファレンスをご覧ください。

単体テストは、_test.go という接尾辞で終わるファイルに保存されています。たとえば、composeNewsletter という名前の関数のテストを行うとします。この関数は、*mail.Message を返します。次の newsletter_test.go ファイルは、この関数の簡単なテストを示したものです。

package newsletter

import (
	"reflect"
	"testing"

	"google.golang.org/appengine/mail"
)

func TestComposeNewsletter(t *testing.T) {
	want := &mail.Message{
		Sender:  "newsletter@appspot.com",
		To:      []string{"User <user@example.com>"},
		Subject: "Weekly App Engine Update",
		Body:    "Don't forget to test your code!",
	}
	if msg := composeNewsletter(); !reflect.DeepEqual(msg, want) {
		t.Errorf("composeMessage() = %+v, want %+v", msg, want)
	}
}

このテストは、パッケージのディレクトリ内から goapp test コマンドを使用して呼び出します。

goapp test

goapp ツールは、App Engine SDK のルート ディレクトリに配置されています。テストを実行しやすくするため、このディレクトリをシステムの PATH 変数に追加することをおすすめします。

aetest パッケージ

App Engine サービスに対する関数呼び出しの多くは、引数として context.Context を必要とします。SDK で提供されている appengine/aetest パッケージを使用すると、偽の context.Context を作成し、開発環境で提供されているサービスを使用してテストを実行できます。

import (
	"testing"

	"google.golang.org/appengine/aetest"
)

func TestWithContext(t *testing.T) {
	ctx, done, err := aetest.NewContext()
	if err != nil {
		t.Fatal(err)
	}
	defer done()

	// Run code and tests requiring the context.Context using ctx.
	// ...
}

aetest.NewContext を呼び出すと、サブプロセスで dev_appserver.py が開始されます。これは、テスト中に API 呼び出しを提供する目的で使用されます。このサブプロセスは、done を呼び出すとシャットダウンします。

基盤となるインスタンスをより詳細に制御する場合は、aetest.NewInstance を使用することもできます。そうすれば、複数のコンテキストを作成して、それらを複数の http.Request オブジェクトに関連付けられます。

import (
	"errors"
	"testing"

	"golang.org/x/net/context"

	"google.golang.org/appengine"
	"google.golang.org/appengine/aetest"
	"google.golang.org/appengine/datastore"
)

func TestMyFunction(t *testing.T) {
	inst, err := aetest.NewInstance(nil)
	if err != nil {
		t.Fatalf("Failed to create instance: %v", err)
	}
	defer inst.Close()

	req1, err := inst.NewRequest("GET", "/gophers", nil)
	if err != nil {
		t.Fatalf("Failed to create req1: %v", err)
	}
	c1 := appengine.NewContext(req1)

	req2, err := inst.NewRequest("GET", "/herons", nil)
	if err != nil {
		t.Fatalf("Failed to create req2: %v", err)
	}
	c2 := appengine.NewContext(req2)

	// Run code and tests with *http.Request req1 and req2,
	// and context.Context c1 and c2.
	// ...
}

詳細については、aetest パッケージ リファレンスをご覧ください。

Cloud Datastore と Memcache のテストを作成する

データストアまたは Memcache を使用するテストコードは、context.Contextaetest パッケージで作成しておくと、簡潔に記述できます。テスト呼び出し aetest.NewContext で、テスト対象の関数に渡すコンテキストを作成します。

SDK の transaction デモ アプリケーションには、テスト性を向上させるコードの構造化例と、データストアを使用するコードのテスト方法が用意されています。

func TestWithdrawLowBal(t *testing.T) {
	ctx, done, err := aetest.NewContext()
	if err != nil {
		t.Fatal(err)
	}
	defer done()
	key := datastore.NewKey(ctx, "BankAccount", "", 1, nil)
	if _, err := datastore.Put(ctx, key, &BankAccount{100}); err != nil {
		t.Fatal(err)
	}

	err = withdraw(ctx, "myid", 128, 0)
	if err == nil || err.Error() != "insufficient funds" {
		t.Errorf("Error: %v; want insufficient funds error", err)
	}

	b := BankAccount{}
	if err := datastore.Get(ctx, key, &b); err != nil {
		t.Fatal(err)
	}
	if bal, want := b.Balance, 100; bal != want {
		t.Errorf("Balance %d, want %d", bal, want)
	}
}

このテストは、goapp test コマンドを使用して実行できます。

goapp test ./demos/transaction

Memcache のテストも同様のパターンに従って実行します。テストの Memcache の初期状態を設定し、テスト対象の関数を実行して、関数によって予期したとおりに Memcache がクエリ / 変更されていることを確認します。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Go の App Engine スタンダード環境