はじめに
Goのinternalパッケージはご存知でしょうか?
本記事では、うまく使うととても便利なinternalパッケージについてご紹介します。
internalパッケージとは
Goパッケージは、基本的には外部のモジュールに公開されます。
パッケージ内のどの識別子を公開するかは、先頭の文字を大文字にするかどうかで決まりますが、パッケージにはその機能はありません。
しかし、Goにはinternalパッケージ(内部パッケージ)という特別なパッケージがあります。internalパッケージは、モジュール外には公開されません。
また、internalパッケージの親パッケージおよびその子パッケージ以下からしか参照できません。
たとえば、以下のようなパッケージ構成があった場合を考えます。
a
├── b
│ ├── d
│ └── internal
│ └── e
├── c
└── go.mod
a/b/internal
パッケージ直下の識別子やa/b/internal/e
パッケージの識別子は、一部のパッケージからしか参照できません。
パッケージb
とパッケージd
以下およびinternal
パッケージ以下のパッケージからの参照に限定されます。
パッケージa
やパッケージc
からは参照できません。
もちろん、internal
パッケージ以下のパッケージで識別子を非公開(小文字で始める)にした場合は、そのパッケージ内だけからしか参照できません。
なお、internal
パッケージはモジュール以下にいくつでも作れます。
テストをしやすくする
筆者は、テストコードはテスト対象のパッケージとは別のパッケージにした方が良いと考えています。
つまり、a
パッケージをテストしたい場合は、テストコードはa_test
パッケージにする方が良いということです。
テストコードを別パッケージにすることで次のような利点が得られます。
- 単体テストがパッケージの初めてユーザになる
- 使い勝手や使用した場合可読性を図りやすい
- 早めに設計ミスなどに気づける
また、一方で次のような煩わしさが残ります。
- 非公開な機能を使ったテストがしづらい
- わざわざ、別のパッケージにするのがめんどくさい
非公開な機能のテストは、export_test.goを使ったパターンでほとんどの場合は解決します。
テストで使用する非公開な機能をAllowリスト形式で並べることにもなるため、テストのときだけの特別扱いを避けやすくなります。
しかし、非公開な機能で複雑な処理をしている場合、どうしてもテストがしたくなります。
その場合に有効なのがinternal
パッケージです。
複雑な処理をしている非公開な機能をモジュール内ライブラリのような形で切り出し、internal
パッケージ以下に配置することで特定のパッケージからしか参照できないように制限できます。
そして、internal
パッケージ以下では公開された機能としてテストすればexport_test.go
を使う必要もありません。
モジュールやパッケージ間を疎結合にする
実はGoチームが管理しているモジュールの多くでinternal
パッケージが利用されています。
公開する必要がない場合はinternal
パッケージ以下に置くようなレベルで管理されているモジュールもあります。
たとえば、Webアプリのようなものであれば、エントリーポイントとなるmain
パッケージを含むパッケージのみ公開し、他のパッケージはモジュール直下にあるinternal
パッケージ以下に配置するという方法がとられます。
pkg.go.devのソースコードを見てもモジュール直下にinternal
パッケージがあることが分かります。
他のモジュールやパッケージから参照されてほしくないパッケージはinternal
パッケージに置いておくと、後方互換を気にせずに破壊的変更が行える利点があります。
ライブラリでもWebアプリでもこの利点は効いてくるでしょう。
おわりに
本記事ではinternal
パッケージの活用について紹介しました。