読者です 読者をやめる 読者になる 読者になる

絶品ゆどうふのタレ

ふと気づいたことを綴るだけのメモ

GolangTestNight(Gunosy.go #10)に参加してきた

http://gunosygo.connpass.com/event/8485/

Goのテストに関する話を聞いてきました。 テスト周りは正直まだまとめ記事読んだ程度しかキャッチアップできていなかったので、周囲がどんな方針でテストしているか、とかも含めて勉強したいと思っていたところ、ちょうどいいタイミングで勉強会があったので参加してきました。

まとめと感想

先に。

  • みんなわりと、素直にGoの用意した仕組みに則ってやってる。
  • とはいえAssertionないのつらいよね、とか、mockの再実装辛いよね、というのは皆思ってるみたい。
  • Web APIで使ってます、というパターン多いみたい。
  • 最終的に懇親会ではインフラの方とopsworksの話で盛り上がってた(ノ∀`)

というわけで、以下メモ。資料URLが見つかったものは貼ってます(`・ω・´)

Goのテストの基本

  • t_matsuwitterさん

そもそもテストとは

  • GOのテスト思想

    • No Assertion
    • No test helper
  • なぜ Assertionがないか?

    • Assertionで中断させるより、クラッシュさせないことを有線
    • 適切なエラーレポートが大切なので、エンジニアがちゃんとエラーメッセージ・エラーハンドリングを実装するべき
  • なぜヘルパーが推奨されていないのか?

    • Go言語自体に十分な支援機構が備わっている
    • 新たなDSLの学習コストにさかずに、ある分だけでテストする
  • Goでのテストの定石

    • 最初に正しい入出力の組を用意して、それが担保されているかチェックする

testing パッケージ

  • *_test.goをgo testで実行する

    • 対象を絞ったりも出来る
  • コード例

    • in outを持った構造体を作ってそれを使う
    • t.Log / t.Error / t.Panicなどを使って、自分で適切なフォーマットを作って伝える
    • t.Errorは実行の途中で止まらずに、最後まで実行される
      • 止まらないほうが、エラーの全体像がわかる。
    • t.Panicは止まる。止まるべきテストの時だけ使うべき。
    • func Test** を実装
  • ベンチマーク

    • func Benchmark** を実装
    • オプションで指定
  • Example

    • func Example()を実装
    • func末尾に //Output: をかいて期待する結果を書いておくと、Exampleとして認識される

Goの開発フローの例

  • スライド参照
  • coverageも簡単に出せるよ

まとめ

  • テストケースに合わせて適切なテストを構築する
  • 大きなフレームワークを導入することなく、モダンな開発フローが作れるよ!

DIからモックライブラリまで

  • daimatzさん

Dependency Injection

  • 依存オブジェクトを直接クラスの中に持たず、外から受け取る

    • インターフェースに対してコーディングできる
    • テストとかモックしやすい
  • 例: Twitter Bot

    • そのまま外部の関数に依存してる
    • 粒度の大きいテストしかできない
  • ではどうするか?

    • 案1: メソッドを引数に渡してみる
      • 責任の切り分けができてない
    • 案2: メソッドをまとめたオブジェクトを渡すようにする
      • 少し楽になったけど、モックできない
    • 案3: インターフェースを渡すようにしておけばいい
    • 案4: さらに初期化時に渡すようにしてみる
      • こうやって、依存オブジェクトを具体的に指定せず、初期化時に注入する方法をDIという
    • 案5: さらに徹底的に
  • 何が嬉しいか

    • Interfaceに対するプログラミングが出来る

アプリケーションの初期化とDIコンテナ

  • アプリケーション初期化問題

    • 起動時に実際にオブジェクトを生成し、順番に注入する必要がある
  • 依存グラフとトポロジカルソート

    • 依存グラフの矢印の逆戻りがないソートのこと
  • これを元に初期化コードをかくと。。。

    • めんどいしミスる
    • なんとかするためのものがDIコンテナ
  • DIコンテナ

  • facebookgo/inject

    • タグを抽出して依存グラフを作って。。。
    • ごりごり
  • (個人的には、アプリケーションの初期化はプログラマが自分で書くべきじゃね?と思ってるらしい)

インターフェース実装によるモック

  • DIする一つの動機として、モックしやすい点がある
  • Java

  • Goの場合はダックタイピングなので、どのインターフェースを実装しているか明示的に書かない

  • 問題点

  • モックライブラリ

    • gomock
    • withmock
  • Javaに比べるとかなり面倒

まとめ

  • gomockむずい

GoとSQLのテスト 理想と現実

  • (スライドどこかな。。。)

App Layerの話

  • SQLを気にせず、テスト時はメモリ上で処理

  • main関数はテスト時には読み出されないので、そこで本番の実装を入れる

  • *_test.goではinitの中でmockする

  • httptest

    • 処理をrecoardできる

現実...

  • 実際のところ、これだとSQL自体はテストしてない。
  • SQLのドライバもモックしよう!

    • go-testdb
  • go-testdbの使い方

    • SELECT文をstub
    • パラメータに応じてstubする場合は全部同じ関数の中でやらないと。。。
    • testdb.Reset()はリセットしてくれない!
    • DRYじゃない。。。!
  • もっと楽な書き方したい

    • mogi
    • Youtubeのvitessのパーサを利用してやる

testifyにPRした話

  • (スライドどこかな。。。)
  • @taichi

testify

  • assertionメソッドをもってる
  • 第1引数がダサい
  • suite.Tがダサい
  • PRしたらなおった
  • みんなPRしよう

Go言語でBluetoothデバイスに接続して環境データを取得してみた

  • data_scientist

F-PLUGとは

  • これをGoと絡めたい
  • GoSerial

gbdでGoデバッグ

  • デバッガを使えば、マルチスレッドを解析しやすい

Goのデバッガ

  • ほぼgdb一択
  • gdbpythonで拡張できる!
    • goにgdb向けの拡張がついてる

使い方

go build -gcflags "-N -l" main.go
gdb main
...
  • 注意点: 本来c用のデバッガなので、メソッド名やレシーバ名をしっかり書かないといけないので注意

  • テストを実行するときは、go test -C でバイナリが生成されるので、それを使うとデバッグできる