自作したパッケージの詳細をGoDocで確認できるようにする
分かりやすいコードを書いていればコメントやドキュメントは不要というエンジニアさんは多いです。
しかし気をつけてコーディングしていても技術者のレベルは様々だし、開発規模が大きくなるにつれ自作のパッケージも増え、なんのために実装したのか不明なコードが出てきて負債になるリスクもあります。
そのようなリスクを回避するためのツールとして gofmt
や go vet
、 golint
などのフォーマッターやリンターがあるわけですが、調べていたら GoDoc
という便利なツールがあったので、情報を共有します。
GoDocとは
GoDocは↓下図のようにパッケージの詳細をブラウザで確認できるツールです。
GoDocの表示例
ルールに沿ってソースコードにコメントを記述することで、パブリックな関数、type、変数についての説明や実装例をよしなにまとめてくれます。
コメントはpythonのdocstringのように一定の規約に沿って記述していくのでコードの可読性を保ったまま整理できます。
GoDocの導入と起動
Goのパッケージなので go get
でインストールします。
go get golang.org/x/tools/cmd/godoc
# 私の開発環境では下記で全てのtoolsをインストールしています。
# go get golang.org/x/tools
下記コマンドで起動し、 localhost:6060
でアクセスできます。
godoc -http=:6060
実装例
ディレクトリ構成
今回は下記のような構成とコードで試しました。
.
|-- cmd/
| `-- godoc-test/
| `-- main.go
`-- pkg/
`-- mypkg/
|-- math.go
|-- math_test.go
|-- say-hello.go
`-- say-hello_test.go
main.go
package main
import (
"app/pkg/mypkg"
"fmt"
)
func main() {
s := []int{1, 2, 3, 4, 5}
fmt.Println(mypkg.Sum(s))
p := &mypkg.Person{
Name: "Hoge",
Age: 1,
}
fmt.Println(p.Say())
}
math.go
package hogehoge
の上にパッケージの説明文を書くとGoDocのOverviewに表示されます。
パブリック関数や変数の前の行にコメントを入れるとGoDocでよしなに表示されます。
/*
Package mypkg is my package.
*/
package mypkg
// Sum returns the sum of a series of numbers.
func Sum(s []int) int {
total := 0
for _, i := range s {
total += i
}
return total
}
math_test.go
テストコードに Example()
という名前で関数を記述すると、GoDocのExampleに表示されます。
package mypkg
import (
"fmt"
"testing"
)
func Example() {
v := Sum([]int{1, 2, 3, 4, 5})
fmt.Println(v)
}
func ExampleSum() {
v := Sum([]int{1, 2, 3, 4, 5})
fmt.Println(v)
}
func TestSum(t *testing.T) {
tests := []struct {
name string
input []int
want int
}{
{"All positive integers", []int{1, 2, 3, 4, 5}, 15},
{"Partially negative integer", []int{-1, 2, 3, 4, 5}, 13},
{"Valid negative integer", []int{1, 2, 3, -4, -5}, -3},
{"All negative integers", []int{-1, -2, -3, -4, -5}, -15},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
output := Sum(tt.input)
if output != tt.want {
t.Errorf("want %!d(MISSING); got %!d(MISSING); input %!d(MISSING)", tt.want, output, tt.input)
}
})
}
}
say-hello.go
package mypkg
// Person is a struct that has name and age.
type Person struct {
Name string
Age int
}
// SayHello returns "Hello!" with Person's name.
func (p *Person) SayHello() string {
return "Hello, I'm " + p.Name
}
// SayHello returns Person's age.
func (p *Person) SayAge() string {
return "Hello, I'm " + string(p.Age) + " years old."
}
say-hello_test.go
package mypkg
import (
"testing"
)
func ExamplePerson_SayHello() {
p := Person{"Hoge", 13}
p.SayHello()
}
func ExamplePerson_SayAge() {
p := Person{"Hoge", 13}
p.SayAge()
}
func TestSayHello(t *testing.T) {
p := Person{Name: "Mike", Age: 3}
tests := []struct {
name string
want string
}{
{"Say person's name", "Hello, I'm " + p.Name},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
output := p.SayHello()
if output != tt.want {
t.Errorf("want %!s(MISSING); got %!s(MISSING)", tt.want, output)
}
})
}
}
func TestSayAge(t *testing.T) {
p := Person{Name: "Mike", Age: 3}
tests := []struct {
name string
want string
}{
{"Say person's age", "Hello, I'm " + string(p.Age) + " years old."},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
output := p.SayAge()
if output != tt.want {
t.Errorf("want %!s(MISSING); got %!s(MISSING)", tt.want, output)
}
})
}
}
結果
最終的に下図のようにドキュメントがまとまります。
まとめ
コードの意味や関数の機能をより理解しやすくするためのコメントやドキュメントを残しておくと、後から見返した時に助かるケースがあります。
GoDoc自体は使わなくても、規約に沿ってコメントを残しておくと良いかと思います。