> Source: https://www.ymotongpoo.com/works/goblog-ja/examples/


# Goにおけるテスト可能なExample

[Testable Examples in Go](https://go.dev/blog/examples) by Andrew Gerrand

## はじめに

Godocの[Example](https://go.dev/pkg/testing/#hdr-Examples)は、パッケージのドキュメントとして表示され、かつテストとして実行することで検証されるGoコードの断片です。
ユーザーがそのパッケージのgodocのWebページを訪れて、関連付けられた「Run」ボタンをクリックすることでも実行できます。

パッケージに実行可能なドキュメントを持たせることで、APIが変更されても情報が古くならないことを保証できます。

標準ライブラリには、こういったExampleが数多く含まれています（たとえば[strings パッケージ](https://go.dev/pkg/strings/#Contains)を見てください）。

この記事では、自分自身のExample関数を書く方法を説明します。

## Exampleはテストである

Exampleはパッケージのテストスイートの一部としてコンパイルされます（そしてオプションで実行されます）。

典型的なテストと同様に、Exampleはパッケージの `_test.go` ファイルに置かれる関数です。
しかし通常のテスト関数と異なり、Example関数は引数を取らず、 `Test` の代わりに `Example` という単語から始まります。

[reverse パッケージ](https://pkg.go.dev/golang.org/x/example/hello/reverse/)は[Goのexampleリポジトリ](https://cs.opensource.google/go/x/example)の一部です。
ここでは、その `String` 関数を示すExampleを紹介します。

```go
package reverse_test

import (
    "fmt"

    "golang.org/x/example/hello/reverse"
)

func ExampleString() {
    fmt.Println(reverse.String("hello"))
    // Output: olleh
}
```

このコードは `reverse` ディレクトリ内の `example_test.go` に置かれるでしょう。

Goのパッケージドキュメントサーバーである*pkg.go.dev*は、このExampleを[`String` 関数のドキュメント](https://pkg.go.dev/golang.org/x/example/hello/reverse/#String)と並べて表示します。

![reverse.String関数のドキュメントに表示されたExample](./pkgdoc.png)

パッケージのテストスイートを実行すると、これ以上何もしなくてもExample関数が実行されることがわかります。

```
$ go test -v
=== RUN   TestString
--- PASS: TestString (0.00s)
=== RUN   ExampleString
--- PASS: ExampleString (0.00s)
PASS
ok      golang.org/x/example/hello/reverse  0.209s
```

## Outputコメント

`ExampleString` 関数が「パスする」とはどういうことでしょうか。

Exampleを実行する際、テストフレームワークは標準出力に書き込まれたデータを取得し、それをExampleの `Output:` コメントと比較します。
テストの出力がoutputコメントと一致すれば、テストはパスします。

失敗するExampleを見るために、outputコメントの文字列を明らかに間違ったものに変更してみましょう。

```go
func ExampleString() {
    fmt.Println(reverse.String("hello"))
    // Output: golly
}
```

そしてもう一度テストを実行します。

```
$ go test
--- FAIL: ExampleString (0.00s)
got:
olleh
want:
golly
FAIL
```

outputコメントを完全に削除すると

```go
func ExampleString() {
    fmt.Println(reverse.String("hello"))
}
```

Example関数はコンパイルはされますが、実行はされません。

```
$ go test -v
=== RUN   TestString
--- PASS: TestString (0.00s)
PASS
ok      golang.org/x/example/hello/reverse  0.110s
```

outputコメントのないExampleは、ネットワークにアクセスするコードのようにユニットテストとして実行できないコードを示すのに便利です。
それでいて、Exampleが少なくともコンパイルされることは保証されます。

## Example関数の命名

Godocは、Example関数をパッケージレベルの識別子に関連付けるために命名規則を使用しています。

```go
func ExampleFoo()     // Foo関数またはFoo型のドキュメントになる
func ExampleBar_Qux() // Bar型のQuxメソッドのドキュメントになる
func Example()        // パッケージ全体のドキュメントになる
```

この規則に従うことで、godocは `ExampleString` というExampleを `String` 関数のドキュメントと並べて表示します。

アンダースコアに続けて小文字のアルファベットで始まるサフィックスを使うことで、1つの識別子に対して複数のExampleを用意できます。
次のExampleはすべて `String` 関数のドキュメントになります。

```go
func ExampleString()
func ExampleString_second()
func ExampleString_third()
```

## より大きなExample

良いExampleを書くには、関数だけでは足りないことがあります。

たとえば[sort パッケージ](https://go.dev/pkg/sort/)を示すには、 `sort.Interface` の実装を見せる必要があります。
メソッドは関数本体の中では宣言できないため、Exampleには、Example関数に加えていくらか文脈を含める必要があります。

これを実現するために、「ファイル全体のExample」を使用できます。
ファイル全体のExampleとは、 `_test.go` で終わるファイルで、ちょうど1つのExample関数を含み、テスト関数やベンチマーク関数を含まず、
かつ少なくとも1つの他のパッケージレベルの宣言を含むものです。このようなExampleを表示する際、godocはファイル全体を表示します。

こちらは `sort` パッケージにあるファイル全体のExampleです。

```go
package sort_test

import (
    "fmt"
    "sort"
)

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%s: %d", p.Name, p.Age)
}

// ByAge は Age フィールドに基づいて []Person に対する sort.Interface を実装します。
type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func Example() {
    people := []Person{
        {"Bob", 31},
        {"John", 42},
        {"Michael", 17},
        {"Jenny", 26},
    }

    fmt.Println(people)
    sort.Sort(ByAge(people))
    fmt.Println(people)

    // Output:
    // [Bob: 31 John: 42 Michael: 17 Jenny: 26]
    // [Michael: 17 Jenny: 26 Bob: 31 John: 42]
}
```

1つのパッケージは複数のファイル全体のExampleを持てますが、1ファイルにつき1つのExampleです。
実際にどうなっているか、[`sort` パッケージのソースコード](https://go.dev/src/sort/)を見てみてください。

## まとめ

GodocのExampleは、コードをドキュメントとして書き、維持するための優れた方法です。
また、ユーザーが土台として使える、編集可能で動作する実行可能なExampleを提供します。ぜひ活用してください！

By Andrew Gerrand

