外部APIだらけのコードをできるだけTDDっぽく作った話

画像の説明

基本的に外部 API 呼び出ししかないコードをいろいろ分解してだいたい TDD っぽく作ったよという話。今回はフレームワークに頼れる部分がなく、全体のパーツを自覚的に整理しながら作り上げていく必要があったので頭の整理をしておく。自分のやっていることはゼロかイチかではなく、概ねどこかしらにテストコードはあるけど網羅はしていなくて、だいたい今回のような感じで進めているような気がする。

だいたいの流れ

クリーンアーキテクチャっぽい円の外側から攻めていった。何しろ思ったように API が動くのか動かないのか分からないと作りようがないので、まずはそこから。1

  • (非TDD)実際の API の動作確認
  • (非TDD)API の response を再利用しやすいようにある程度パターン別に収集(テストコードを利用してアプリケーションの一部だけを動かす)
  • (TDD)API アクセスのコードの一部を stub out しながら API Client 部分を実装
    • できる範囲で API の動作を mock サーバや emulator で閉じ込めて「分離」
  • (TDD)API に依存しつつアプリケーション全体の文脈を強く表すボキャブラリーとインターフェイスを用いた Facade 的なものを用意2

ここまでは stub / mock / emulator でどうにかなった。

気をつけていたのは Gateway 的な部分で、例えばストレージ系の API を呼ぶ必要のある API Client の部分は Repository パターンのような汎用的なインターフェイスを用意しつつ、もう一つ内側の Gateway の部分では Repository をあまり意識させず、単にやりたいことをメソッドとして持つようにしておいた3

どうにかならない部分は処理の流れ的にほぼ後行程にしかないので文字通り後回しにした。これはそうなるようにアーキテクチャを選んでいたからできたことでもある。

そしてようやくアプリケーション全体の流れ。

  • (非TDD) Application Logic (API の初期化と処理全体の流れ)の実装。最低限の初期化周り以外はほとんど自動テストしていない。

今回これで回せた理由は以下の二つだと思っている。

一つめは、そもそも今回はアプリケーションをサーバレスのピタゴラスイッチで実現することにしたこと。一つ一つのアプリケーションの役割は十分に小さいのでミスが入りにくい。

もう一つは、流れに関するロジックを図で言うところの Gateway か Domain に押し付けるようにしたから。

例えばアプリケーションロジックのように見えるものでも API 呼び出しの比率が高いものは Gateway の中に閉じ込めることで API 側で回せている TDD の中で完結させることができるならできるだけそうしたのだ。全体の流れとしては何らかのメソッドを呼んでいるだけにする。

逆に完全にプレーンなオブジェクトだけで表現可能なものは、

  • (TDD)プレーンなオブジェクトに対して API に依存するオブジェクトからメソッドを通じて値オブジェクトを渡す形で独立させてロジックを実装

していった。

この部分が最初から見えていてプレーンなオブジェクトに抽出できていたらドメイン駆動っぽいのだけど、残念ながら自分の場合は API call 部分からどうやって testable にするか順番に引き剥がしていって、全体の流れのロジックを書く段階になって完全に逆サイドにロジックを押し付けるという戦術に結果的になった。

まとめ

アプリケーション全体を以下のように分解して考えることで、外部 API call だらけのアプリケーションでも開発の中盤はだいたい TDD できることが分かった。

  • そもそものアプリケーションの流れを小さく分解
  • API call 部だけの TDD
  • API call とアプリケーション全体の流れの中間に入る部分の TDD(Gateway)
  • 一切の API call と距離を置くプレーンなオブジェクトでロジックを記述する部分の TDD

今回は手元で再現できる emulator や mock サーバ、抽象化するライブラリのある API を自分で選ぶことができたが、そうでない場合はテスト環境を TDD のための装置として

  • まずは deploy と環境構築の自動化

からやる必要があるだろう。それすらできない場合は…どうするのがいいんだろう。

  1. 逆にここに一切不安がないなら中心からやってもよいと思う 

  2. クリーンアーキテクチャっぽく言うと Gateway のようなもの 

  3. 例えばの話であって、Gateway 相当の部分が Repository パターンの方がよい場合もあるだろう。 

More

Categories

Tool 日々 Web Biz Net Apple MS ことば News Unix howto Food PHP Movie Edu Community Book Security Text TV Perl Ruby Music Pdoc 生き方 RDoc ViewCVS CVS Rsync Disk Mail FreeBSD Cygwin PDF Photo Zebedee Debian OSX Comic Cron Sysadmin Font Analog iCal Sunbird DNS Linux Wiki Emacs Thunderbird Sitecopy Terminal Drawing tDiary AppleScript Life Money Omni PukiWiki Xen XREA Zsh Screen CASL Firefox Fink zsh haXe Ecmascript PATH_INFO SQLite PEAR Lighttpd FastCGI Subversion au prototype.js jsUnit Apache Trac Template Java Rhino Mochikit Feed Bloglines CSS del.icio.us SBS qwikWeb gettext Ajax JSDoc Rails HTML CHM EPWING NDTP EB IE CLI ck ThinkPad Toy WSH RFC readline rlwrap ImageMagick epeg Frenzy sysprep Ubuntu MeCab DTP ERD DBMS eclipse Eclipse Awk RD Diigo XAMPP RubyGems PHPDoc iCab DOM YAML Camino Geekmonkey w3m Scheme Gauche Lisp JSAN Google VMware DSL SLAX Safari Markdown Textile IRC Jabber Fastladder MacPorts LLSpirit CPAN Mozilla Twitter OpenFL Rswatch ITS NTP GUI Pragger Yapra XML Mobile Git Study JSON VirtualBox Samba Pear Growl Mercurial Rack Capistrano Rake Win RSS Mechanize Sitemaps Android JavaScript Python RTM OOo iPod Yahoo Unicode Github iTunes God SBM friendfeed Friendfeed HokuUn Sinatra TDD Test Project Evernote iPad Geohash Location Map Search Simplenote Image WebKit RSpec Phone CSV WiMAX USB Chrome RubyKaigi RubyKaigi2011 Space CoffeeScript Nokogiri Hpricot Rubygems jQuery Node GTD CI UX Design VCS Kanazawa.rb Kindle Amazon Agile Vagrant Chef Windows Composer Dotenv PaaS Itamae SaaS Docker Swagger Grape WebAPI Microservices OmniAuth HTTP 分析基盤 CDN Terraform IaaS HCL Webpack Vue.js BigQuery Middleman CMS AWS PNG Laravel Selenium OAuth OpenAPI GitHub UML GCP TypeScript SQL Hanami Develop Document Jekyll