先日、SPMというコマンドラインツールをリリースしましたが、golangでコマンドラインツールを作成する上で便利だったツールや考え方等を備忘として残します。

CLIのベース

3rd Partyのライブラリを利用せずとも標準のflagパッケージで処理を書けますが、色々と面倒なのでurfave/cliのパッケージを利用しました。このパッケージは以下のように、簡単にサブコマンドやフラグを定義できます。

エラーハンドリングはActionの関数の戻り値にerrorを入れれば良く、errorがある場合にはHandleExitCoderを呼び出します。HandleExitCoder内ではErrWriter経由でerror#Error()が出力され、OsExiterが呼び出されます。デフォルト値はErrWriterがos.Stderr、OsExiterがos.Exitなので、そのまま使うと標準エラー出力に出力して終了コード付きでプロセスが終了します。ErrWriterとOsExiterは外部公開されているので、他のio.Writerや関数をセットすれば柔軟にエラーハンドリングが可能です。

YAMLのロード

設定のロードでYAMLを利用する際はgopkg.in/yaml.v2を使います。

使い方はxmlやjsonのUnmarshalと同じく、structを定義してマッピングします。

ロガー

標準のロガーであるlogパッケージはレベル付きのロギングをサポートしていなかったり、フォーマッティングは自分でやらないといけなかったりと不便なので、3rd PartyのSirupsen/logrusを使いました。

使い方はこんな感じ。

あるいは

のような使い方でもOK。前者の方法は一つのチャネルにしかロギングできませんが、後者の方法であれば複数のloggerインスタンスを作成できるため、複数のチャネル(例えばstdoutとstderr)にロギングすることができます。

実際には、このloggerを直接使うのではなくラップした独自のloggerを定義して利用した方が変更に強い実装になりそうです。

テスト

DIの話と同様に、入出力のストリームを置き換えできるようなインターフェースにしておくとテストが楽です。CLIの場合は入力のストリームはコマンドライン引数であり、出力ストリームは標準出力になります。これらをテスト時には文字列の配列、Bufferに置き換えることになります。

SPMだとロガーのデフォルトはStdout、Stderrにしており、テスト時はbytes.Bufferに書き込むようにしています。StdoutはFileの型なので、コマンド利用時とテスト時の両方に適用できる型として、io.Writerのインターフェースを指定していることに注意してください。

またテストはstretcher/testifyのライブラリを利用するとアサーションが楽です。

参考URL