golang

golangでテストコード書くならgotestsパッケージを使うのもいいかも

golang-testcode-write-gotests-eyecatch

golang大好きエンジニアのつじたくです。

みなさんテストコード書いてますか?本当は書いたほうがいいことはわかってるけど

  • テストコードを書く時間を工数に入れてなかった
  • めんどくさい
  • そもそも書く気がない
  • etc・・・

など様々な理由からテストコードを書いてない(書けない)人も多いのではないでしょうか。

今日はそんな人におすすめなgotestsパッケージを紹介したいと思います!

golangはテストコードを書きやすい

そもそもgolangはテストコードが書きやすくなっています。

その大きな理由はgolangが標準でテストパッケージを用意してくれてるからだと思っています。

テスト用に追加でgo getしたりインストールしたりする必要がなく、テストコード自体の書き方も特別なものではないので普通にgolangを書いている人であればテストコードの理解できて、学習コストがものすごく低いです。

つじたく
つじたく
テスト用フレームワークとかの書き方を覚えへんでいいし

めっちゃいいやん

簡単に書けるので試しに実装してみます。

// 必ず失敗するテスト
package main

import "testing"

func Sum(a, b int) int {
	return a + b
}

func TestSum(t *testing.T) {
	if Sum(1, 1) != 1 {
		t.Error("miss!!!")
	}
}

https://play.golang.org/p/BbhRP6f04i1

上記は必ず失敗するテストコードになっています。以上でテストコード実装は終わりです。簡単ですね!

ただし幾ら簡単に書けるからといって、実際のプロジェクトでテストコードを書いていると様々な問題が出てくると思います。その中で私が最近問題視しているのが

  • テストコードの書き方が人によってバラバラ・・
  • 推奨されているTableDrivenTests(※後述)の書き方になっていない

の二点です。

この問題をある程度解決してくれるのがgotestsパッケージです。

gotestsパッケージ

github

githubはこちらです。

テストコードを自動生成してくれる

gotestsパッケージは一言で言えばテストコードを自動生成してくれるパッケージです。

しかも単純にコード生成してくれるだけではありません。

多くのエディタに対応してる

READMEに記載してありますが、多くのエディタにプラグインとして対応しているようです。

The following shows gotests in action using the official Sublime Text 3 plugin. Plugins also exist for Emacs, also Emacs, Vim, Atom Editor, Visual Studio Code, and IntelliJ Goland.

 

私が観測している範囲ではgolangを書くときに、GoLandを使っている人が多いのですがGoLandにも対応してくれているのはうれしいですね!

 

推奨されているTableDrivenTests

golangではテストコードの書き方としてTableDrivenTestsが推奨されています。(公式wikiにも記載あり)

テストコードを書く人によってはTableDrivenTestsを知らない人もいるのでテストコードがバラバラになりやすいのですがgotestパッケージを使うことによってテストコードがある程度統一されるので、その心配はなくなります。

つじたく
つじたく
推奨されている書き方でコード自動生成してくれるのはありがたい泣

使ってみる

サンプルの構造体を用意してメソッドを生やしておきます。

Sumメソッドに対してgotestsパッケージを用いてテストコード自動生成を行います。

// 構造体
type Sample struct {
	a int
	b int
}

// Sumメソッド
func (s *Sample) Sum() int{
	return s.a+s.b
}

 

自動生成されたテストコードは下記になります。

実際テストコードとしてrunさせる場合は少し手を加える必要があるのでテストコードとしては不完全ですが、自動生成されたコードを見てもらいたいのでご了承ください。

// 自動生成されたテストコード
func TestSample_Sum(t *testing.T) {
	type fields struct {
		a int
		b int
	}
	tests := []struct {
		name   string
		fields fields
		want   int
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s := &Sample{
				a: tt.fields.a,
				b: tt.fields.b,
			}
			if got := s.Sum(); got != tt.want {
				t.Errorf("Sample.Sum() = %v, want %v", got, tt.want)
			}
		})
	}
}

 

ちゃんとTableDrivenTestsに沿ってコードが生成されてますね!

あとは少し手直しすればテストコード完成です。

TableDrivenTests

TableDrivenTestsについて少し触れたいと思います。

恐らくなんですがTableDrivenTests自体はgolang以外の言語でも推奨されてるんじゃないかな〜と思ってます。

(他の言語に関しては調べてないですすみません)

TableDrivenTestsとはデータのパターンを変えながら同じテストを回すことで、可読性が増すといった書き方です。

下記のコードを公式wikiからコードをコピペしてきました。

golangでの書き方はテスト用構造体を用意して、その中にテストケースを挿入しforで回すだけです。

// 公式wikiから引用
var flagtests = []struct {
	in  string
	out string
}{
	{"%a", "[%a]"},
	{"%-a", "[%-a]"},
	{"%+a", "[%+a]"},
	{"%#a", "[%#a]"},
	{"% a", "[% a]"},
	{"%0a", "[%0a]"},
	{"%1.2a", "[%1.2a]"},
	{"%-1.2a", "[%-1.2a]"},
	{"%+1.2a", "[%+1.2a]"},
	{"%-+1.2a", "[%+-1.2a]"},
	{"%-+1.2abc", "[%+-1.2a]bc"},
	{"%-1.2abc", "[%-1.2a]bc"},
}
func TestFlagParser(t *testing.T) {
	var flagprinter flagPrinter
	for _, tt := range flagtests {
		t.Run(tt.in, func(t *testing.T) {
			s := Sprintf(tt.in, &flagprinter)
			if s != tt.out {
				t.Errorf("got %q, want %q", s, tt.out)
			}
		})
	}
}

 

まとめ

gotestsパッケージを使うと

要点
  • 人によるテストコードのバラツキが少なくなる
  • 推奨されているTableDrivenTestsの書き方が出来る

いいことづくめなのでぜひみなさん使ってみてください〜!