Technology Engineering

178inaba の技術ブログ

Go言語で画像生成のテストを書く #golang

この記事はGo3 Advent Calendar 2019の18日目の記事です。

この記事ではGo言語における画像生成のテスト方法を紹介します。

画像生成というのはロジックに手を入れていなくてもライブラリがアップデートされたタイミング等でバグりやすい箇所です。
テストを用いてデグレの確認を常に行いましょう。

サンプルアプリ

今回の説明のために文字を画像に文字を印字するCLIを書いてみました。

github.com

こんなコマンドで

$ print-on-image -d 'hello, world' -o example.jpg

f:id:i178inaba:20191217152601j:plain
こんな感じの画像が作られる

このアプリを例に説明します。

テスト対象は run という関数です。
引数に印字する文字列 drawValue と印字した画像を保存するWriter w を取ります。

func run(drawValue string, w io.Writer) error {
    if drawValue == "" {
        return errors.New("draw value is empty")
    }

    baseImg, err := getBaseImage()
    if err != nil {
        return err
    }

    fo, err := getFont()
    if err != nil {
        return err
    }

    drawImg := drawStringToImage(baseImg, fo, drawValue)
    return jpeg.Encode(w, drawImg, &jpeg.Options{Quality: 100})
}

テストコード

14行目でテスト対象の関数を呼んでいます。
その際に bytes.Buffer を引数の io.Writer の部分に入れていることがわかると思います。
これは後で画像のbyteスライスを取得するためです。

その後は

  • テスト画像の生成
  • 実際のテスト

の2ルートに分かれます。

テスト画像の生成

最初はテスト画像が無い状態ですのでテスト画像の生成を行います。
環境変数 IS_CREATE_DST_FILEtrue に設定してテストを実行します。

$ IS_CREATE_DST_FILE=true go test

すると createDstFile が実行され、テスト画像の生成が行われます。

createDstFile は最後に t.Skip を実行しています。
これはテスト画像生成の場合は createDstFile が終わったら終了してほしいためです。
(テスト画像生成の場合は実際のテストの実行はしない。)

テスト画像が生成されたら目視確認をして問題ないかを確認します。

# Macの場合
$ open _testdata/dst.jpg

テスト画像の生成は初回1度で良いと思われるかもしれませんがコードは残しておくことをおすすめします。
以前Goのバージョンが上がったときに画像生成のロジックが変わっており、生成される画像も目視では変わっていないように見えるものの、byte配列は変わっていました。
また、画像の生成ロジックをちょっと変更したいという時にもテスト画像の生成を使って出力される画像を確認しながら開発を進めることができるので便利です。
以上のような場合にテスト画像をすぐ作れるようにしておくためにもコードは残しておくと良いです。

実際のテスト

テスト画像を作成したら実際のテストを実行します。
実際のテストは IS_CREATE_DST_FILE に何も設定しないでgo testを実行します。

$ go test

22行目でテスト画像である _testdata/dst.jpg ファイルを読み込み、26行目でbyteスライスを取得。
31行目で引数に入れた bytes.Buffer から Bytes でbyte配列を取得して bytes.Equal を使用して比較します。

golang.org

今回はそれほど大きくない画像ファイルなので ioutil.ReadAll ですべてのbyte配列を読み込んでいますが、大きいファイルの場合は Read から数バイトずつ読み込んで比較するという方法を取ったほうがメモリ使用量が少なくなるはずなのでよいと思います。

まとめ

画像生成のテストを紹介しました。
これにより画像生成ロジックにデグレがあった場合でもすぐに分かるので安心してライブラリのアップデートを行うことができます。

また、テスト画像の生成を気軽に行えるようになったことで画像生成の開発も楽になるのではないでしょうか。

ぜひ皆さんのアプリでも実践してみてください。