Help us understand the problem. What is going on with this article?

testeratorを使ってgae/goのunit testを高速化する

More than 3 years have passed since last update.

testerator はGoogle App Engine for Go(以下gae/go)のUnit Testを高速化するために生まれたライブラリです。

gae/goはサーバのSpinUp速度がJava, Python, PHPに比べて高速なので、最近App Engine Developerたちの間に人気ですが、いかんせんUnitTestの遅さだけは洒落にならないレベルで遅いです。

それを緩和するために生まれたのが、 testerator です。
testerator がやってくれることを理解するためには、まずgae/goのUnitTestがなぜ遅いのかを理解する必要があります。

gae/goのUnitTestが遅い原因は、テスト環境がgae/pythonの環境に間借りしているからです。
例えば、公式のサンプル にある通り、 aetest.NewContext() を利用すると、裏ではgae/pythonにあるgaeのmockのプロセスが立ち上がります。
このプロセスの立ち上がりには3secほどかかります。これがUnitTest開始時に1度だけで済めばよいのですが、環境をクリアするために、UnitTest毎にプロセスの再作成が行われます。
そのため、UnitTestが100個あると、3sec * 100で300secかかります。

UnitTestを実行する度に、珈琲を豆から挽いて淹れることができるレベルです。

import (
        "testing"

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

func TestWithContext1(t *testing.T) {
        ctx, done, err := aetest.NewContext() // ←ここで3sec
        if err != nil {
                t.Fatal(err)
        }
        defer done()

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

func TestWithContext2(t *testing.T) {
        ctx, done, err := aetest.NewContext() // ←ここでも3sec !
        if err != nil {
                t.Fatal(err)
        }
        defer done()

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

testerator はgae/pythonのプロセスの立ち上がりを抑制してくれる

testerator はUnitTestのスピードを上げるために、一度立ち上げてgae/pythonのプロセスを使いまわすようになっています。

import (
    "testing"

    "github.com/favclip/testerator"

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

func TestPut(t *testing.T) {
    _, c, err := testerator.SpinUp() // gae/pythonのインスタンスが無ければ起動、あれば使いまわす
    if err != nil {
        t.Fatal(err.Error())
    }
    defer testerator.SpinDown() // プロセスをシャットダウンせずに、Datastoreなどの内容をクリアする

...
}

func TestGetEmpty(t *testing.T) {
    _, c, err := testerator.SpinUp()
    if err != nil {
        t.Fatal(err.Error())
    }
    defer testerator.SpinDown()

...
}

testerator 利用時の注意点

testerator を使ってUnitTestを高速化するためには、最初にgae/pythonのプロセスを1つ立ち上げる必要があります。
UnitTest毎に testerator.SpinUp() , testerator.SpinDown() を交互に呼ぶだけだと、結局プロセスを毎回作り直してしまうからです。
無難な方法としては、 TestMain(*testing.M) を使って、UnitTest前に testerator.SpinUp() を呼んで、UnitTest後に testerator.SpinDown() を呼びます。

import (
    "fmt"
    "os"
    "testing"

    _ "github.com/favclip/testerator/datastore"
    _ "github.com/favclip/testerator/memcache"
    _ "github.com/favclip/testerator/search"

    "github.com/favclip/testerator"
)

func TestMain(m *testing.M) {
    _, _, err := testerator.SpinUp() // 最初の1プロセスを起動!

    if err != nil {
        fmt.Printf(err.Error())
        os.Exit(1)
    }

    status := m.Run() // UnitTest実行!

    err = testerator.SpinDown() // 最初に立ち上げたプロセスを落とす
    if err != nil {
        fmt.Printf(err.Error())
        os.Exit(1)
    }

    os.Exit(status)
}

それでも完璧には早くならなかったよ・・・

testerator はUnitTestの高速化に貢献してくれました。
しかし、たくさんのUnitTestがある時は、爆速にはなりません。
なぜならば、gae/pythonのプロセスをずっと使いまわすると、途中で応答が無くなってしまうので、定期的にgae/pythonのプロセスを再起動しているからです・・・。

何回UnitTestを実行した後にプロセスを再起動するかは ResetThreshold という値が管理されており、デフォルトは15です testarator.go#L52

変更したい場合は testerator.DefaultSetup.ResetThreshold に設定を行います。
もし、なんかよくわからんけど、UnitTestの応答が無くなったら、ちょっと減らしてみると良いかもしれません。

func TestMain(m *testing.M) {
    _, _, err := testerator.SpinUp()
    if err != nil {
        fmt.Printf(err.Error())
        os.Exit(1)
    }
    testerator.DefaultSetup.ResetThreshold = 12

    status := m.Run()

    err = testerator.SpinDown()
    if err != nil {
        fmt.Printf(err.Error())
        os.Exit(1)
    }

    os.Exit(status)
}

さいごに

ここまで読んだあなたはきっと"これって README.md に書いてあったほうがいいんじゃない!?"って思ったことでしょう。
僕もそう思います。
...がんばって、英語書いてpull req送っておきますね。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした