テスト駆動開発を逸脱する瞬間を体験したのでメモ

f:id:saikou9901:20180107213904p:plain

OSSとして(まだ個人ですが)開発中の DomainDictionary というアプリで、テスト駆動開発を試みています。(まだ実装部分はリリースしてないのでmasterではないですが)

テスト駆動開発 の細かい説明はしません。簡単に言うと。

  • 新しい機能を作る前に、その機能のテストコードを先に実装する(この時点では必ずテストエラーになる)
  • 実装したテストを成功させるために、最低限必要な実機能を実装する
  • 次のテストコードを実装する
  • ・・・

といった(かんじかな〜と勝手に解釈している)開発手法です。

なかなか安定感があり、自分に合っているかな〜とおもってここ2週間くらいは順調に実施できていました。

しかし今日、ついに機能実装を先にやってしまうという禁忌に手を染めてしまったので、その経緯をメモがてら書いていきます。

初めてテストを書いてみたとき

テストコードを書くこと自体については「ケースの網羅よりも、どうテストを実装するかに集中してしまい、品質が下がってしまうことがある」と言う話はよく聞いていました。

実際に手をつけてみると、なるほど、その通りでした。

今までテストを実装したことがなかったのですが、最初は覚えることだらけでした。

  • 結果のassert
  • MockBeanの作成
  • MockMVCの作成
  • Mock呼び出しの引数などのSpy
  • ・・・

などなど、手動で行なっていたテストをコードに起こすと、いろいろ痒いところが多かったです。

そのために、本当の最初には機能を先に実装しておき、ググってはテストを書き、成功するはずのテストが失敗してはまたテストの書き方を試行錯誤する、ということを繰り返していました。

プレゼンテーション層、アプリケーション層、インフラ層など各層の単体テスト(しかも正常系のみ)を一通り書き切った段階で、やっとテストコードを書くこと自体に慣れたな〜という感じでした。

思ったテストを一発で書けるようになったところで

ここでやっと、テスト駆動開発と言われるもののスタートラインに立ちました。

WebAPI側のテスト

これは簡単。

MockMVCを使って、まだ何も実装していないURLに対してリクエストをこない、そのレスポンスを検証するコードを書けばいいだけです。

最初の実行結果は、もちろん「404 Not Found」になります。

そっから下全部

ここから先がなんとも複雑な感じになりました。

まずはプレゼンテーション層から呼び出している、サービス層のモックアップを作成しよう・・・というところで、 when(サービスクラス.method(args)).thenReturn(mockResult) するための、そもそもの サービスクラス.method(args) が未定義です。

テストに失敗するまえに、そもそもコンパイルが通りません。

ということで、 サービスクラス.method(args) を実装しよう・・・とするのですが、これは「テスト駆動開発」です。つまり、次に書くコードは「サービスクラス.method(args)のテストコード」になります。

ということで、サービスクラスのコードを書きますが、今度はその下のインフラ層の when(リポジトリクラス.method(args)).thenReturn(mockResult) が書けません。

というこで、次は リポジトリクラス.method(args)を・・・

となります。

TDDすると実装する順は

こんな感じで実装していきました。 (私の走り出しプロジェクトでやっているので、プロダクトやチームの成熟具合では全然違ってくるとは思いますが・・・)

  • プレゼン層のフロント側のテスト
  • プレゼン層のフロント側の機能(ここでプレゼン層半分できた)
  • アプリ層のプレゼン層側のテスト
  • アプリ層のプレゼン層側の機能(ここでアプリ層半分できた)
  • プレゼン層のアプリ層側のテスト
  • プレゼン層のアプリ層側の機能(ここでプレゼン層完成)
  • インフラ層のアプリ層側のテスト
  • インフラ層のアプリ層側の機能(ここでインフラ層半分できた)
  • アプリ層のインフラ層側のテスト
  • アプリ層のインフラ層側の機能(ここでアプリ層完成)
  • インフラ層内部(DBまわり)のテスト
  • インフラ層内部の機能(ここでインフラ層完成)

わけわかりませんね。

ですが、テストが通ることにより、機能ができあがったという安心感はすごく感じられて、これまでに新しい達成感がありました。

そして逸脱へ・・・

と言う感じで今日までやってきましたが、ついに問題がおきました。

それは 「機能の実装方法を試行錯誤しなければならないから、実装が完了するまで検証コードが確定できない」というものです。

また、SpringBootに依存した実装でRestControllerからレスポンスを返す際に、「どんな値やヘッダ値が入ってくるかわからないから、一度動作をさせてみないと検証コードがわからない」ということもありました。


「私はリリースをあきらめない。TDDをあきらめます。」


テストを書かずに機能実装のみを行うと

すばらしいです。

みるみるうちに新しい機能ができあがっていくんです。さながら、違法薬物に手を染めているようです。

そして、新しい機能ができあがると、アプリが動いてとても達成感があります。

そうすると、なんということでしょう。また新しい機能が書きたくなるんです!

そう、テストを書かずに機能実装のみを行うのは、本当に 薬物中毒 のようです。

ひとまず完全に中毒に陥らないように

テストクラスだけは必ず作成し、必要と思われるテストをメソッド定義+@Testアノテーションだけは記述し、中身はTODOコメント、と言う状態で進めています。

早いとこ機能を書き終えて、テストを書こう。

というわけで

みなさんも、TDD逸脱の悪循環にご注意ください。

おわり。