Golang こんな時どうする?
Yusuke Komatsu
2016-10-04.DMM.comラボ & eureka & インテリジェンス合同 Go言語勉強会
NULLを許容するDBやJSONって どうしよう?
• ポインタを使う
• sqlパッケージの型を使う(sql.NullString とか)
• NULLなにそれ?おいしいの?
sql.Null** を使った場合
• sqlパッケージの中で実装されている
• Nil判定もできるし、実態となる型はそのまま(Int64ならInt64で値が格納されている)
• Scanを通せばある程度ゆるく解釈してくれる
• 1変数内(構造体)に2つのパラメータが格納されていて、両方見ないと値を取得できない
利点
欠点
ポインタを使った場合
• 数値や文字列などと一緒にNilを使える
• Nilを区別しないで使うことができる
• 通常の型として扱うには元に戻す必要がある
• JsonなどをDecodeする時にゆるい解釈ができない(IntをStringとして解釈するなど)
利点
欠点
NULLを許容問題はRDBだけじゃない
• 他のデータベースやキャッシュする際も考慮が必要
• jsonやXMLなどでも使う(特にAPI)
• 他言語のプログラムと通信する場合も考える必要がある
便利な独自型つくりました
JSONのEncode/Decodeが直感的
同じ値でもsetする型によって柔軟に解釈
実は某社のAPIのレスポンスで事故ったので作った• 複数のサービスのデータを取りまとめられていて、サービスごとに型が
バラバラ • int型のパラメータにstring型が混ざっていたりする • APIもPHPで、型やハッシュor配列判定を勝手に吸収して出力されている • 静的言語にとっては死亡フラグ
こんな感じだったので またこんなことが起きても対処できるように作りました
genericはいろんな方のいいとこ取り• Scanが定義されているのでsqlパッケージでも意識せず使える • MarshalJSON / UnmarshalJSONを定義してるのでEncode/Decode時のNULL
は自動的に考慮される • Set関数をつかえば、Nilを考えずに変数に値をセットできる • Value関数を使えば、1つだけ値が返ってくる(Nilの場合はNilが返ってくる) • 型判定の柔軟さはPHP並 • reflectはエラー時のみしか使ってない • sql.Null**型でやっているようなNULL判定も可能
実はGo 2系でGenericsが実装されるかも
http.Response.Body 消失問題しってますか?
• http.Response.Bodyを使いまわそうとしても1度使うと空のio.ReadCloserになってしまう
• 変数に入れ直しても変数も消える
こんなことはできません
こうしたら解決できる
みなさんテストってどうしてます?
• とりあえずgo test?カバレッジとってる?
• ソースの品質管理(担保)ってどうしてます?
• そもそもテストしてますか?
リポジトリ内の全パッケージをテストしたい
• リポジトリのルートディレクトリから再帰的にテスト
• vendorディレクトリ配下はテストしたくない(glide nv)
• 品質管理のためにカバレッジも取りたい(coverprofile)
go test $(glide nv) -coverprofile=coverage.out
ERROR:
cannot use test profile flag
with multiple packages
リポジトリ内の全パッケージをテストしたいROOT_PATH="$GOPATH/src/seeds.rickcloud.jp/bitbucket/scm/hm/wage" COV_PARTIAL_FILE=profile.cov.out COV_FILE=profile-all.cov.out COV_MODE=count HEADER="mode: $COV_MODE"
cd $ROOT_PATH; \ # テスト用DBのマイグレーション
goose -env testing up; \
echo "mode: count" > $COV_FILE # ひつようのないディレクトリを除いたディレクトリを再帰的にチェック
for dir in $(find . -maxdepth 10 -not -path './.git*' \ -not -path '*/_*' -not -path './cmd' -not -path './release*' \ -not -path './vendor*' -type d) do if ls $dir/*.go &> /dev/null; then # 個々にテストをして一時保存用プロフィールに保存
go test -v -covermode=count -coverprofile=$dir/$COV_PARTIAL_FILE $dir if [ -f $dir/$COV_PARTIAL_FILE ]; then # 一時保存されたものを出力用プロファイルに転記
cat $dir/$COV_PARTIAL_FILE | tail -n +2 >> $COV_FILE rm $dir/$COV_PARTIAL_FILE fi fi done # テスト用DBのロールバック
goose -env testing down; \
ソースコードの品質管理• gofmt (インストール不要)
ソースコードを整形する
• go_vet (インストール不要)コンパイルエラーにならないヒューリスティックな問題を検出する
• gocyclo (https://github.com/fzipp/gocyclo)循環的複雑度(コードの複雑性)の検証をする
• glint (https://github.com/golang/lint)Golangのlinter
• ineffassign (https://github.com/gordonklaus/ineffassign)無駄な割当を検出する
• misspell (https://github.com/client9/misspell)スペルミスを検出する
ソースコードの品質管理
ソースコードの品質管理
THANK YOU!![generics]
• usk81/generic https://github.com/usk81/generic
• proposal: generic programming facilities https://github.com/golang/go/issues/15292
[test] • Go Report Card
https://goreportcard.com