はじめに
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パッケージの活用について紹介しました。