PactのProvider側の検証ができたのでいったんまとめ

三連休の初日というタイミングにも関わらず Kanazawa.rb の meetup で真面目に初めてのツール Pact を試し、現状の報告をするという真面目っぷり。今日は Pact の Provider 側の動作が一応分かったので現時点でのまとめを書いておく。

realestate-com-au/pact: Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.

※ 実際にはこのあとさらにもう一回練習したうえで見えてきたことも混ざってるんだけど、そこはそれ

全体像

+------------------------------------------------+
| consumer                                       |
|                                                |
| spec/                                          |
|  +- service_provider/                          |
|  |   +-pact_helper.rb                          |
|  |   +- {service_name}_{consumer_name}_spec.rb |
|  +- pacts/                                     |
|      +- {pactfile}                             |
+------------------------------------------------+

  (interaction = request ↓ + response ↑ )

+-------------------------------------------+
| provider                                  |
|                                           |
| spec/                                     |
|  +- consumer_name/                        |
|  |   +-pact_helper.rb                     |
|  |   +- {service_name}_provider_states.rb |
|  +- pacts/                                |
|      +- {pactfile}                        |
+-------------------------------------------+

登場する用語

  • consumer
  • provider
  • mock
  • mock_service
  • interaction
    • given == provider_state
    • upon_receiving == description
    • with == request
    • will_respond_with == response

consumer が API の client 側、provider が API の server 側、そして request と response の対が interaction で、これが基本のセット。

client ( consumer ) に必要な機能

一つ大きな特徴というか、そもそもこれが狙いなんだけど、consumer のテストは実際のサーバには繋ぎにいかない。そのために consumer の通信先、API の endpoint を自由にスイッチさせることのできる機能が必要になる。 これを使って Pact の consumer のテストの際には endpoint を mock_service に向けてテストを行う。

ちなみに Pact の example に使われているのは HTTParty というライブラリで、これは consumer クラスに HTTP 関連のクラスメソッドを include する。endpoint の設定には base_uri というメソッドを用いる。

jnunemaker/httparty: Makes http fun again!

mock_service の正体

想像通りの Rack アプリ。spec を流す際に裏で Rack アプリを立ち上げて、立ち上がるまで待って、RSpec の DSL で書いたリクエストを pact-support の中で組み立て直している。

一貫しているのだが、なかなかそれに気付けなかった点

特に自分がなかなか飲み込めなかった点を書いておく。Pact の考え方はちゃんと整理されているのだが、用語ばかりが先に立って考え方がなかなか入ってこない部分があったので、そのメモを並べていく。

interactionの相手をまず特定してからテストを書くこと

Pact でのテストは まず interaction の相手を特定するところから始まる というのを覚えておくと理解が早い。

Pact のテストは基本的には RSpec ベース1ということで spec/ 以下に、ファイルを置いていくんだけど、例えば consumer 側のテスト(これが provider 側では contract になる)を書きたい2場合は

spec/
 +- service_providers/
      +- pact_helper.rb

を作るところから始まる。consumer が contract を書きたい provider を特定するところから始まるのだ。だから consumer 側のテストは service_providers/ というディレクトリの中に入る。(はず)

同様に、サーバ側のテストは

spec/
 +- service_consumers/
     +- pact_helper.rb

という名前で、自分(provider)の contract を定義している consumer を特定して行く格好になる。

provider, consumer, provider_state の名前重要

基本的には consumer で contract を書いて → provider を verify する順でテストが進んでいくのだが、この時にどことどこの名前を合わせておく必要があるのかを理解していないとちゃんと動かない。

しかも名前が合っていないというエラーが出るわけではないのでますます注意が必要だ。

まず consumer 側で contract を書いていくわけだが、その時に出てくるのが

Pact.service_consumer {consumer_name} do
  has_pact_with {provider_name} do
    mock_service {mock_name} do
      port xxxx
    end
  end
end

  1. contract を書こうとしている consumer の名前
  2. 次に interaction を行う provider の名前
  3. 最後に mock service の名前を決め、どの port で動かすかを決める

という形になっている。これで実際に contract を書く時には

{mock_name}.given({provider_state}).
with(request params).
will_respond_with(respose params)

という mock の定義を行うわけだが、ここで mock_name が一致していないとちゃんとサーバに繋がらないのでテストを行うことができない。

provider_state というのは provider がどんな条件の時にどういう動作をするかを記述するためのもの。実際の provider_state の記述は以下のような感じになる。

Pact.provider_states_for {provider_name} do
  provider_state {provider_state} do
    no_op
  end
end

特に何も準備しなくて済むならよいが、DB の準備やら何やら必要な場合はここで state を再現する。

例えば GET / POST / PUT / DELETE のすべてにおいて、「すでに該当するデータが存在するかどうか」は一つの重要な state として挙げられるだろう。それによって API の response が変わるならばそれを state として記述していくことで、より実践的な contract になっていく。

Ruby以外の場合

自分が Pact に注目しているのは『マイクロサービスアーキテクチャ』で勧められているから、そしてクックパッドが取り上げていることと自分が Rubyist であるからだが、Ruby 以外の WebAPI も Pact でテストは可能である。

consumer 側

consumer の mock の動作を他の言語や RSpec 以外の DSL で記述するためのツールがいくつかある。例えば以下のように

以下を見ると他にもいろいろ充実しつつあります。

Introduction · Pact

..NET も Go も見える。

provider 側

provider 側も上のリンク先から探してもいいし、そもそも Pact の mock service は Rack アプリなんだから Rack で工夫できるよねということで

bethesque/pact-provider-proxy: Allows pact verification against a running provider at a configurable base URL

こんなのもある。これは rack-reverse-proxy を使って Rack アプリのテストをしてるフリしながら別サーバにリクエストを投げさせてテストする 格好になるので、アプリ側はどんな言語で書いてもオッケー。その代わりテストは Ruby で書いてね、という寸法。

provider_state の準備方法はあれこれ工夫が必要になりそうではありますが…。

[2016-07-31 追記] ある程度の受け入れテストはこれでできそう。厳密なものはクライアント側の方を集めにしないとダメかな?

Pactがあれば他のテストが不要になるわけではないが責務の分割を考えやすくなる

最後に今のところの理解のまとめと感想を。

Pact は API の interaction と contract に注目した一種のテスティングフレームワークであり、逆にそれ以外のところには関知しない。

すごく初歩的なところで言えば endpoint の切り替え機能。mock に向けられるようにしておくというのはすでに書いたが、mock じゃなくて production と staging の切り替えができるようにしたい場合もあるだろう。この機能は provider と consumer の間で取り決める contract の範囲外だ。したがって RSpec なり Minitest なり他のテストと同じように準備して同じようにテストする。

また API client 側であれば consumer のテストとは別に Model のテストも必要になるだろう。API client という名前では曖昧になってしまう責務が consumer としてくくり出されることで、逆にテストも実装もやりやすくなるように感じた。

これまで自分の書いたものはあまりそういうことを意識していなかったように思う。サーバサイドはすでにフレームワークが充実していて request を受け取り終わったあとの処理を書くことに特化しているので、リクエストを受け取る処理とその後の処理に分割するということを普段の実装では意識することがない。クライアントサイドも公開されている API に対してだけ書くか、ごく単純な GET くらいだったので、基本的には現物合わせをするだけで、request / response だけを責務としてテストするということはそんなに考えていなかった。

Pact はちょっと準備が面倒なところはあるし Ruby を普段使っていないとさらに面倒なところはあるが、なんとか wrap して使うことはできるので、今後は積極的に使っていくことでアプリを無駄に複雑にせずに済みそうな気がしている。

良いものを知った。

勇気づけられたやりとり

なかなかこういうの相談できる人いないので、こういうツッコミはとても助かります。

参考

  1. consumer 側は minitest で書く実装もある 

  2. 実際、最初にこれを行うのが肝要 

窓から投げ捨てても大丈夫なのか

ドイツ男性、窓からパソコン落下もおとがめ無し | ワールド | Reuters

警察のスポークスマンは「誰にでもそう感じた経験があるのでは」と語り、パソコンを投げ落とした男性の気持ちに共感を示した。

いやーだからってなぁ…。

あとお約束だけど OS とアプリは何使ってたの?

メールに対する正しいウィルススキャンの方法とは?

えーと最近の fw とか anti-spy とかそういう系統のことは何も考えていません。あとサーバ側で動作するアンチウィルスのことも考えてません。とりあえず MUA 限定。

メールについてウィルススキャンするタイミングって、

  1. 受信時
  2. 送信時
  3. メールボックス操作時
  4. メールをプレビュー、あるいは開くとき
  5. 添付ファイルを開くとき

くらいかなと思う。

で、いま個人的にいちばん問題だなと感じているのは 3 のメールボックスを操作した瞬間のスキャン。多くのメールソフトでメールボックスは「1フォルダ
1ファイル方式」であり、つまりフォルダが数百MB とか GB クラスのオーダーに達している場合、メールボックスの操作をするたびにそれだけの巨大なファイルを常にスキャンしている状態になるはずである1

それって使いものになるの?

普通に考えてただでさえ重たい巨大なファイルの操作なのに、そこにウィルススキャンが常に割り込んでくるという状態で、果たしてメールソフトをまともに使えるのだろうか? というか、実際に全然使いものにならなかったんで、メールボックスの入っているフォルダをリアルタイムスキャンの除外対象にしたところ、信じられないくらいに軽くなったんだけど、

この対処ってやっちゃダメ?

なのかどうかがイマイチ判断できないのが困ったところ。

上のリストで「メールボックスの操作」を、「メールをプレビュー、あるいは開くとき」とわざわざ分けているのは、メールボックスの操作には例えばフィルタによってメールを自動的に振り分けるとか迷惑メールを判定してジャンクフォルダに振り分けるなどの操作も含まれるからであり、またこれはユーザーが毎回手で操作するものではないので、「うっかり感染」の可能性の低い動作だと思うからである。可能性が低いと思っているからリアルタイムスキャンの対象から外したわけだけど、これを外しちゃった場合にどこまで影響が出るのかイマイチつかめていないので不安だ、というのがこのエントリの趣旨なわけ2

基本的にメールとウィルスの関係は

  1. メールソフトそのものの脆弱性を利用したウィルス
  2. 添付ファイルにウィルスを付加して他のアプリから感染
  3. URLハンドラ周りの脆弱性を利用したウィルス

の3パターンだと思うんだけど、恐らく上の対処をした場合、1 のメールソフトそのものの脆弱性によるウィルス感染を防ぐことが難しくなると思う。

ただし、その場合も「受信時とメールボックスへの保存時の間」でスキャンを掛けてくれれば問題ないような気がするんだな。要するにアンチウィルスソフトがメールソフトのプラグインのような形で動作するか、あるいは pop proxy として動作していれば、超巨大なメールボックスファイルを毎回相手にしなくても安全性を確保できるんじゃないかと。

だから、メールソフトのプラグインや pop proxy, smtp proxy として動作するアンチウィルスソフトであれば、メールボックスのファイルを他のアプリの使うファイルと同じようにリアルタイムスキャンしておかなくても、安全性はあまり損なわれないと言えるかなと思うんだけど、どうだろう?

どうでしょうか?

教えてエロい人!

というか、重いのとは別に「1フォルダ : 1ファイル方式」のメールボックスに対してリアルタイムスキャンを掛けるのって、

誤作動した場合のダメージがでかすぎる

と思う。それだけ考えてもメールボックスファイルそのものへのリアルタイムスキャンは外した方がいいように思うんだけど、どうでしょうか。いやね、メールソフト以外でも開くかもしれないじゃんという反論はすぐに自分でも思いつきましたよ。でもねぇ、現実に全然まともな速度で動かないんだもの。多くのメールソフトの実装が「1フォルダ
1ファイル方式」なんだから、こういう工夫ってアンチウィルスソフト側で考えるべきことじゃないの? なんでユーザーが悩まなきゃいけないんだ。
  1. 基本的にウィルススキャンてのはファイルの I/O に対して行われるから。 

  2. もちろん影響範囲がメールソフトの作りによって変わるのは分かってるんだけど。 

とりあえず古い記事はツッコミ不可に

週末留守にしていたらコメント spam にやられた。フィルタに手を加えるヒマはちょっとないので単純作業で

  1. spam を除去
  2. キャッシュを全削除
  3. あまりに古い記事は *.tdc を書き込み不可に
  4. ついでに役に立たない自作フィルタの適用をやめる1

てな流れにして、まず tDiary のアップデートから始めるべきかも、との思いに至る。

  1. TrackBack を受けられるように直すのも今は面倒なのだ 

Panther と Ruby

  • Emacs
  • Ruby

両方入っているのになぜ ruby-mode.el が入っていないのだ。

OS X Gecko 自衛策

userContent.css で

input, select {
	font-family: Osaka, sans-serif !important;
}

やっぱサイト制作者としては CSS は動的に補正を加える方式にしないとダメかねぇ。

userChrome.css で

#urlbar {
        font-family: Osaka, sans-serif !important;
        line-height: 1em !important;
}

なんかも効果的。

ヒラギノや平成は「送り」の部分がグリフ内にすでに入っているので逆に細かい調整や他の要素との兼ね合いが難しくなっている。楽なのはナビゲーションの入るサイトのタイプフェイスにはこれらのフォントを使わず、印刷用 CSS などで利用するってことかなぁ。ほんとはナビゲーション要素に限定して Osaka を利用する方がいいんだろうけど、、、やっぱちょっと不便だなぁ。

ダイアログボックスとかいじろうと思ったが、まともな資料がない! 設定項目を grep で抜き出すだけとか、最低でもソースのどのファイルを読めとかってあってしかるべきだと思うんだが、そういうこともしてない。

仮想デスクトップ

裏で作業させっぱなしの terminal とか、立ち上げておくけど普段目につく必要のない irc とか、そういうものがあると便利な感じする。あと、たくさんのアプリを立ち上げておいて、デスクトップアイコンをドロップさせたいとか。

でも Expose はドラッグ中にも有効なのであった。よくできてるなぁ、Expose

実は Windows で仮想デスクトップを使っているとフローティングのツールバーとかが迷子になることがあるので、Windows にも Expose があった方がほんとは便利なのかもしんない。一画面で作業するならウィンドウ重なりまくりなので X-Mouse は要らないな。OS X に X-Mouse の機能がないのは、ウィンドウを閉じることとアプリの終了がイコールじゃないってのも大きいのかな。そう考えるとやっぱ Mac って一貫してるんだな。

9ヶ月ぶりくらいに自分の環境を一から作り直した。

と言っても Windows 2000 を入れて SP 4 を当てた以外は今までのアプリのうち必要なさそうなものを入れなかったくらいで、変わったことは何もしていない。

そこで気づいたことがあった。

まずトラックポイントドライバ。Space Saver Keyboard II のドライバのファイルを探してみたのだが思ったようなものが見つからない。ファイルサーバにそれらしいフォルダが掘ってあって exe が放り込んであった。型番を見るとどう考えても ThinkPad 用のドライバ。

やってみたらすんなり動いた。どうも ThinkPad 用の TrackPoint ドライバが PS/2 互換だったことを思い出して PS/2 の Space Saver Keyboard なら動くだろうと推測して入れたらしい。うーん、どこにもメモが書いてなくてビビったが、面白いことしてるな、おれ。

もうひとつは仮想デスクトップ。なんだかんだで WinVirtual に落ち着いているのだが、これが思い通りの場所に思い通りの設定で落ち着いてくれない。仕方なくレジストリを直接編集して設定したのだが、自分がこんな設定方法のツールを使っているのが実に意外だった。

Duron 800 から Celeron 850 になったが、全体的なパフォーマンスはなんか Duron の方が高い気がする。SP4 になったおかげで以前より動きがスムーズな部分もあるだろうし、余計なサービスはまだ動いていないのでもう少し速さを感じてもよさそうなものなのに、実際には瞬発力では Celeron の方がもっさりしている。キャッシュの違いかな? あと、ディスクアクセスが遅いような。。。ビデオのモード切替がやたらと遅いような。。。 i815 はやっぱダメですか?

1152 * 864 にしたらリフレッシュレートが 75Hz までしか出ず、ちらついてやめてしまった。うーん、このモニタも困ったモニタだ。

そういえば。

以前から謎だ謎だと騒いでいたパスワードが見えちゃう件は AltIME にそういう設定がされていました。。。 自分でやったんだろな。

About

例によって個人のなんちゃらです

Recent Posts

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 Document SVG AsciiDoc Pandoc DocBook Develop Jekyll macOS Node.js Vite Heroku Transformer AI Data Cloud Wasm