Upgrade to Pro — share decks privately, control downloads, hide ads and more …

テスタビリティの高いGoのAPIサーバを開発しよう

Akito0107
March 06, 2021

 テスタビリティの高いGoのAPIサーバを開発しよう

Akito0107

March 06, 2021
Tweet

More Decks by Akito0107

Other Decks in Programming

Transcript

  1. 目次 • Chapter 1: 
 • Testabilityについて
 • アプリケーションをを動かしてみよう (Codelab)


    • Chapter 2
 • アーキテクチャとTestabilityについて
 • アプリケーションをリファクタしてみよう (Codelab)
 • Chapter 3
 • Test Doubleについて 
 • テストのサンプルを書いてみよう (Codelab)
 • まとめ

  2. 講師紹介 • 氏名:伊藤 瑛(@akito0107)
 • 所属:DeNA SWETグループ
 
 • 業務内容


    • Go言語プロダクトの開発効率化と品質向上
 • ゲーム基盤の運用開発
 • etc...

  3. 講師紹介 • 氏名:金子淳貴(@theoden9014)
 • 所属:DeNA SWETグループ
 
 • 業務内容:
 •

    Go言語プロダクトの開発効率化と品質向上
 • ゲーム開発プロセス改善
 • etc...

  4. Testabilityとは • 「テスト容易性とは可視性と操作性である」
 「ソフトウェアの動作を監視したり,状態を操作したりする機能を組 み込めば,よりテストが容易になる。」
 セム ケイナー,ジャームズ バック,ブレット ペティコード. ソフトウェアテスト293の鉄則

    (Japanese Edition) より
 
 => システムの「状態」を操作・シュミレートできるかがTestabilityの1 つの要素
 
 
 *ISO/IEC 25010などの他の定義もあります また、「試験性」と訳されることもあります。
  5. 例) • ユーザを登録するAPI
 • email addressの重複は許さない仕様
 
 • Testの際に再現すべき状態
 •

    email addressが重複するような状態を作り出してtestする必要 がある
 • Test時にこの状態をどれだけ簡単に安定して
 作り出せるか

  6. リファクタリングについて • 「ソフトウェアの外部の振る舞いを保ったままで、内部の構造を改 善していく作業」
 Martin Fawler. 新装版 リファクタリング 既存のコードを安全に改善する より


    • APIの外側からの振る舞いを担保する仕組みが必要
 (Testが必須)
 • 今回は簡単なAPI Testを実装し、振る舞いの担保を行ってみます
 • API Testの実装の際に、Data(=状態)をどう扱うかを観察してみ てください。

  7. 3層アーキテクチャ実装の2つのポイント • (1) ひとつ下の層にのみ依存する
 • 例えば、presentationはBusiness Logicにのみ
 依存する。
 presentationが直接Data Access


    を呼び出してはいけない
 • (2) それぞれの層は具象的な実装ではなく、
 抽象に依存する
 • Goの場合はinterfaceを使う

  8. Dependency Inversion func main() { str, err := PrependHello(os.Stdin) if

    err != nil { log.Fatal(err) } fmt.Println(str) } // os.Fileという具体的な型に依存している func PrependHello(f *os.File) (string, error) { var buf bytes.Buffer if _, err := io.Copy(&buf, f); err != nil { return "", err } return "hello " + buf.String(), nil } • Keyboard入力に対して helloを追加するコード • Keyboard以外にはどう対 応する? • PrependHelloをどうテスト するか?
  9. Dependency Inversion func PrependHello(r io.Reader) (string, error) { var buf

    bytes.Buffer if _, err := io.Copy(&buf, r); err != nil { return "", err } return "hello " + buf.String(), nil } • 具体的な実装ではなく抽象(interface)に依存する
  10. Dependency Inversion func TestPrependHello(t *testing.T) { actual, err := PrependHello(bytes.NewBufferString("test"))

    if err != nil { t.Fatal(err) } if actual != "hello test" { t.Errorf("expected 'hello test' but actual %s", actual) } } • Test時にはos.Stdinの代わりにbytes.Bufferを使える • (もちろんTest以外にもいろいろなioに対応できるというメリットがある)
  11. (参考) 間接入力・間接出力 • 間接入力
 • 依存コンポーネントからテスト対象への入力
 • 例: RepositoryからDBのデータ読み出し
 •

    間接出力
 • テスト対象から依存コンポーネントへの出力
 • 例: RepositoryからDBへのデータ書き込み

  12. TestにおけるDBについて • 「When there is any way to test without

    a database, test without the database!」
 Meszaros, Gerard. xUnit Test Patterns: Refactoring Test Code (Addison-Wesley Signature Series (Fowler)) . Pearson Education. より
 • Test Dataのsetup / cleanupや並列化の問題など...
 • Docker / Libraryの充実により、上記の問題の一部は解決されつ つあるが、細かいロジックのハンドリングなどはDBを使わずにTest できると楽
 • 詳しくはTechCon2021の「自動テストのないプロダクトの開発効 率化への道」を見よう

  13. スタブ実装例: 現在時刻の差し替え Testコード func TestAddDurationToNow(t *testing.T) { currentNow := Now

    Now = func() time.Time { tm, err := time.Parse(time.RFC3339, "2021-03-06T15:00:00Z") if err != nil { t.Fatal(err) } return tm } defer func() { Now = currentNow }() expected, err := time.Parse(time.RFC3339, "2021-03-06T16:00:00Z") if err != nil { t.Fatal(err) } actual := AddDurationToNow(1 * time.Hour) if !actual.Equal(expected) { t.Errorf("expected: %s but actual: %s", expected, actual) } }
  14. スパイ実装例: httptest.ResponseRecorder func main() { handler := func(w http.ResponseWriter, r

    *http.Request) { io.WriteString(w, "<html><body>Hello World!</body></html>") } req := httptest.NewRequest("GET", "http://example.com/foo", nil) w := httptest.NewRecorder() handler(w, req) resp := w.Result() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(resp.StatusCode) fmt.Println(resp.Header.Get("Content-Type")) fmt.Println(string(body)) } https://play.golang.org/p/y_M3RA0YUDB
 

  15. (参考) maintainableなtest codeへ • 今回Codelabで実装しcodeは重複が多く、保守性(=maintainability) が高いとは言い切れない
 • 実際にはTable Drive TestingやTest

    Helperの切り出しなどを行っ て、Maintainabilityの高いTest Codeへ修正していく作業がある
 • Test CodeのMaintainabilityも気にするようにしよう

  16. References • セム ケイナー,ジャームズ バック,ブレット ペティコード. ソフトウェア テスト293の鉄則 (Japanese Edition)


    • Martin Fawler. 新装版 リファクタリング 既存のコードを安全に改善 する
 • Robert C.Martin,角 征典,高木 正弘. Clean Architecture 達人 に学ぶソフトウェアの構造と設計 (Japanese Edition) 
 • Meszaros, Gerard. xUnit Test Patterns: Refactoring Test Code (Addison-Wesley Signature Series (Fowler)) .