Sinon.JSにもう少し詳しくなったのでちょっとまとめ

Sinon.JS - Standalone test fakes, spies, stubs and mocks for JavaScript. Works with any unit testing framework.

対象バージョン

Sinon 7.5.0

背景

  • これを書いている人はテスティングフレームワーク組み込みの double より独立しているものの方が好み
    • その方が寿命が長い傾向にあるため
  • これを書いている人はもともと Ruby で RR をよく使っている
  • Ruby の場合は property はそもそも private
  • public なのはすべて method
  • ただし Ruby の method は定義時も呼び出し時も () を付ける必要がなく、引数がない場合は property アクセスと同じように見える

Sinonの基本

  • Sinon の double はもともと存在している property に対してしか書けない
    • TypeError: Cannot stub non-existent own property <name> エラー
    • RR では存在していようがいまいが関係ない
  • Sinon の double は一回一回ちゃんと restore しないといけない
    • 同名のdouble に対して double をもう一度定義できない
    • TypeError: Attempted to wrap <name> which is already wrapped エラー
  • Sinon のドキュメントでは生成した double の object をレシーバにして restore しているが、sinon.restore() でもよい

Sinonのdoubleはmethodに対して機能する

Ruby と RR の組み合わせに慣れているとちょっと「あれ?」と思ってしまうが、以下の書き方を見ると納得。ここでは stub を例に挙げる。

sinon.stub(obj, <method>).returns(<val>)
sinon.stub(obj, <method>).callsFake(<func>)

ここでうっかり property を対象に stub を書いてしまっても定義はできるが property アクセスした際の値には影響しないし、method として call しようとすると not a function で TypeError になる。

では property を諦めて全部 method にしないといけないかというと、そうではなくて、これは getter method なのだと見做すとよい。正解はこう。

sinon.stub(obj, <method>).get(() => <val>)

何らかの値を return する function を get に与えてやればよい。

上に書いたように Ruby は property のように見えるものも method だし、method は式なので、RR の場合は return するか call するかのような区別は必要ない。これらの Ruby の API が DSL を作る際に非常に有利に働いていることがよく分かる。

stub().get()はなぜか多重定義できる

上に書いた原則に反するし、これを知っていても別に得しないが、

sinon.stub(obj, <method>).get(() => <val>)

に関しては restore なしに複数回実行できる。

callback, Promise地獄のJSでは純粋な関数とSpy重要

Promise は書いた順番通りに実行されないし、debugger でも完全に自由に break できるわけでもない。そこで

spyObj = sinon.spy(obj, <method>)
..
assert(spyObj.called)

のようにしておくと、どのメソッドが呼ばれたか ≒ どのルートを通っているかなどの確認ができるし、これが可能なように細かく純粋な関数を増やしておくと非常にテストしやすくなるのでオススメ。1

  1. 純粋な関数がよいのは sinon とか double とかに限らない普遍的なテクニック 

ちょっとテストとか価値とかについて思ったこと

昨夜カフェでこんな話をしていた。

自分はこんなことを喋った。

  • テストがあると安心
  • とりあえず Selenium だけでもあると違うと思うけど、HTML をテストするときはデータの違いでテストが丸ごと落ちたりしやすいから使いどころ難しいんだよね。サイボウズさんはよく Selenium ベースで回せたよなー1
  • 最近教えてもらって納得したのは、スローテスト回避にムキになって mock でかくするのは本末転倒っぽいこと。テストコードのメンテコストを下げるために fixture replacement が効いたので最近 mock ざっくり捨てたお。

相手はこんな話をしていた(誤解あったらごめん。というかボカす必要あるかな)。

  • テストコード(のメンテ)がプロダクトコード変更の足を引っ張らないか
  • RSpec より Cucumber の方がクライアントの納得や価値に繋がらないか
  • ぶっちゃけ細かいテストが揃っていることよりクライアントへの価値提供の方が大事じゃないか

自分はこの話にあまり食い違いを感じていない。(相手がどう受け取ったかは分からないけど。)

今日はその話を書こうと思う。

自分が言ってるテストは TDD のテストであり、今のところテストの網羅性にそこまでこだわってないし2、どうしてもテストしにくい部分やちょっとの変更で落ちやすいテストで、かつ別にテストがなくてもそんなに不安を感じない部分はそもそもテストを書いてない。例えば View はほとんどテストしてないし JavaScript も DOM 操作がほとんどなのでそんなにテストしてない。3

テストコードがプロダクトコードの変更の「邪魔」になるのは、その変更が本当にクリティカルなら仕方ないかなと思うし、軽微な変更であればセーフティネットになってくれるので心軽く変更していけると感じている。4

また、書き始めのときはテストコードを書くことでメソッドを外から見直すことができて「あぁこれは変だな」と気づきやすくなる。こういうのは直すなら書き始めのうちに直した方がよい。まだ実際には使われていないのだから、影響範囲を考える必要がない。

で、実はこの「安心」が大事だと思っている。

自分にとってももちろん「自分(たち)の提供できる価値」はここ何年か考えているテーマの一つだ。ただ、あまりそこは声高に言わずに来ているような気もする。というのも開発者が自分たちの仕事に「安心」を手に入れれば、他の今まで考えていなかったこと、例えば「価値」だったりあるいは「より良い仕事」などについて考えやすくなるのではないかと思っているから。先に「負担」を減らすことで、より視野を広げることや様々なアクションを起こしやすくなることを期待している。5

ユニットテストを自動化するようになってたぶんもう6年くらいになる6んだけど、ざっくり振り返るとその間に以前よりチャレンジできる幅が広がったような気がする。もちろん経験が増えただけで生まれた余裕はあるかもしれないんだけど、単純な話、この間に自分の能力は基本的に上がっていないと思う。むしろ落ちている。加齢だ。もともと徹夜などはできないタチだが、明らかに体力は落ちていて、以前のようには無理が効かない。以前から肩こりはあったが、もっと幅広い範囲に悪影響が出る。回復も遅くなった。そしてトシをとると、生活の中や職場の中で使える時間はどんどん少なくなってくる。

それでも、できることは増えたと思う。

テストだけでなく GTD 的なものを取り入れたおかげで、効果的に忘れることができるようになっているという面もある。ライフハックにはそれほど熱心な方ではないが、この数年で手に入れたものが体力の低下や集中力の低下を補っている感覚はある。

もちろん周囲の期待の変化などもあると思うが、以前よりは周りの話を聞く余裕も少しは生まれた。ま、そうでないとできない仕事が増えたという意味でもあるんだけど。まだまだ本当の余裕はなく、正直きつい毎日だけど、それでも「書けば書くほど、作れば作るほど不安が増える」という状況ではないことが、幅を作る余裕として支えになっている部分があると思う。

なんだろうな。守りを堅くしてから攻めに出るというか、そういう感じなのかなーと思っている。自分(たち)の中に安心と自信が少しでも多くあった方が「価値」の説得力が増すんじゃなかろうか。そういう感じ。

残念ながら自分の仕事の成果は人に誇れるほどたいしたものはない。だからここに書く言葉に説得力を感じる人はあまり多くないと思う。例えば自分がはてなやクックパッドのトップエンジニアならまったく違った印象を持ってもらえるかもしれない。でもそうじゃない「ふつう」の人間が地に足をつけて「ふつう」の仕事を進めることもできなくはない、その一例として記しておこうと思う。

いや、「できている」と自信を持って言うことは難しいので断言はしないけどね。7

  1. 実は自分も去年フロントを Selenium でテストするためにあれこれやってみたけど、テスト用のデータをこっちから読んでーとかアレコレいじる必要があったし、最終的に満足のいく感じにはならなかった。 

  2. このさじ加減とニュアンスを共有するのは難しいし、自分が間違ってる可能性はおおいにある 

  3. 書いたのは URI によって DOM 操作が変わる部分。URI の解釈を目視テストだけでいくのは厳しいのでテスト書いた。あるいは自分が例えば UI エンジニアならまた違ったアプローチになると思う。 

  4. だいたい予想通りのテストが落ちる。予想外のテストが落ちると「あぁなるほど」と思う。 

  5. もちろんこれまで書いていないテストを新たに書き始めることはその瞬間では負担増だけど。 

  6. うわ。なのにまだCIまで行ってないよ! 

  7. 実際のところ最近はつい7ヶ月前に作ったレガシーコードと戦ってるし。これは完全スピード重視でログと目視とほんのちょっとのテストコードだけで突き進んだやつを再利用しているから。 

IP messenger に四苦八苦

昨日から IP messenger を使おうとして四苦八苦している。IP messenger については実は興味はあったが試したことのなかったアプリがあって、それが

今までプロトコルと UI を分離してる実装はお目にかかったことがなかったのでこれは面白そうだと思ったわけ。

IP messenger は基本的に Windows アプリで、それ以外のプラットフォームにも様々な移植版が存在する。本家 Windows 版を使っている分には LAN 内での軽めのメッセージのやりとりに非常に重宝するのだが、他のプラットフォームではやはりクライアントの完成度が落ちてしまう問題があった。

Un*x には上に挙げた kipmsg の他にも X 版、GNOME 版がある。でもさすがに古すぎる。Java 版があるのも知っていたがここは最も新しいものを試したい。結果、

早すぎたんだ

最初 deb 用のファイルリストが用意されていることに気付かずに GNOME で動かしている Ubuntu 上で必死に KDE の環境をインストールしていたがあえなくコンパイルできず泣きそうになった。が、実はまったくそんな苦労は必要なかった。しかし、ちょっと使ってみたらウィンドウのちらつきが気になったり、問答無用で refresh が走ってユーザーを選択できないとか、refresh が走るたびにメンバーリストのカラムの幅がリセットされるとか、終わりのない我慢大会だったのでこれは諦めた。

最後に見つけたのがペタクローン。これはいい。Java なので最初の起動は重たいが、起きてしまえば結構快適に動く。タスクトレイ的な場所のアイコンの他にどうしてもウィンドウが存在してしまうのがうざいが、それを我慢させてしまう強力な機能がある。

メンバーのインクリメンタルサーチ

もう一度言う。これはいい。本家 ML でもメンバーの検索したいですと言ったことはあるんだけど、検討されている様子はまったくなかった。でも IP messenger のインターフェイスなら検索は必須だと思うんだよな。irc みたいに nick 補完でメッセージ投げるわけにいかないんだから。

ただ最初戸惑ったのが、メンバーリストはメッセージを作成してから [ 送信 ] というアクションを起こさないと出てこないこと。IP messenger と互換クライアントをいくつか試してきたなかで、このタイプは初めてだった。これまでの記憶ではみんな送信ウィンドウでメッセージの作成とユーザーの選択を同時に行えたはず。

で、まぁ検索の仕組みまで入るとゴチャゴチャしちゃうからかなぁと思ったけど、この方式には実は致命的な問題もある。

メッセージを投げようとするまで相手がクライアントを立ち上げているかどうか確認できない

メンバーリストを最初から見ることができればメッセージを作成してしまう前に相手の存在を確認できる。しかし現在のペタクローン方式だとどうしても先にメッセージを作成する流れになるので空振りになる危険性がある。そこだけもったいないなと思う。メンバーリストを任意のタイミングで自由に確認できるようになったらいいな。

Ubuntu でそこら中 Emacs 風に

MacOSX の自分にとってよいところはいたるところで Emacs 風の編集が有効なこと。で、当然 GNOME でもできると思ったけど、標準ではできないっぽい。

gconf-editor

図のようなこんな感じで gconf-editor から /desktop/gnome/interface/gtk_key_theme を emacs にすると良い。

Be My Last ヘビーローテ

今さらながら宇多田ヒカルはいいな。歌がうまいだけの小娘だった頃には少しも興味が湧かなかったが、結婚する前くらいと、そして最近はやはりよい感じ。

でもコレ当時は売れなかったらしい。

恐るべきは自分の「マイナー」センサー。か?

tramp で sshx で multihop

;;「で」が multihop.

実は何度も何度も試してみていたんだけど、どうにも sshx で multihop できないなぁと悩んでいた。どうも sshx は multihop に使えるメソッドとして登録されていないっぽい。

各ホップは ホップメソッド 指定、つまりユーザ名とホスト名から成ります。ホップメソッドはインラインメソッドのみになり得ます。以下のホップメソッドが(今のところ)利用可能です:

  • telnet
  • rsh
  • ssh
  • su
  • sudo

※ 各メソッドの説明は省略しています。

ということでマニュアルの

sshx
お分かりのように、これは ssh とちょっと違った類似物です。 ssh がリモートホストで通常の対話的シェルを開くのに対し、このオプションは `ssh -t -t host -l user /bin/sh' を使って接続を行ないます。この方法は、通常のログインシェルが、ログインの際に質問をしてくるように設定してある場合に有用です。この手順によりそれらの質問を回避して、`標準的な' ログインシェルを tramp に使わせることができます。

の記述を頼りに .emacs に

(require 'tramp)
(setq tramp-default-method "sshx")
(add-to-list
  'tramp-multi-connection-function-alist
  '("sshx" tramp-multi-connect-rlogin "ssh -t -t %h -l %u /bin/sh%n"))

と書いてみたら繋がった1。うむうむ。これやっぱ補完できないと不便だね。次は補完か。

cf.

  1. このとき、ユーザー名は省略できない。ちょっと引っ掛かった。 

Camino にした

Camino - MozillaのパワーとMacのエレガンス

XUL がね、遅いんですよ、OSX では。Windows とか Linux とか使ってる人は意外と快適だと思うんですけど、OSX 版の Firefox は明らかに他のブラウザより遅いんです。

それでも我慢してました。Mozilla の頃からのつきあいですもの。でもねー、試しに Camino 動かしてみたらすっげー速いの。そら速いだろうなとは思っていたけどもさ。Safari の速さを思えばこれくらい速くなっても不思議はないなと思ったけどもさ。いやー速いのなんの。

というわけで Camino に移行決定。webdeveloper はじめ各種 extension は、普段ただ Web 見てるときには必要ないしね。diigo と del.icio.us と bloglines の bookmarklet が動けば問題ないことが分かった。userContent.css はそのまま使えるし、user.js もだいたいうまく動く。bbs2chreader と QuickNote の extension は惜しいけど、2ch はそもそも最近ほとんど見ないし、そのままスルー1、QuickNote は何か他のものを探せばいいや。もともとブラウザが常に立ち上がっているはずだからブラウザに依存したメモソフトを使うって発想になっただけで、これでなきゃいけないわけじゃない。スキンも最近はまったく凝ってなかったので気にしない。

いやーはえーはえー。Mozilla から Firebird に乗り換えたときくらいかそれ以上の衝撃。速い。

※ デフォルトでは command + クリックでタブじゃなくて新規ウィンドウで開いちゃうけど、これを変更すればほぼ違和感なし。あ。「長押し」ができない。これ結構痛い。あとタブの入れ替えができない。うむむ。

Macの手書き説明書 | CaminoSession

なんてものが。そうか忘れてた。あとは単体の RSS リーダが要るな。Camino は 1.1 で auto-discovery には対応するらしいけど、その時点でもリーダの機能はつかなさげなので、ローカルのフィードを確認する方法がない。

うげ。

Diigolet は単体じゃ動かないのか。ツールバーの機能を使って個人の設定を読み取って他に登録しようと考えているブックマークサービスを列挙する形になっているっぽい。つまり、Diigo にだけ post する分にはツールバーは要らないが、del.icio.us に同時に投げようと思ったらツールバーが要る。

うーむ。困ったな。

  1. RSS リーダーに登録しちゃうと気になるので登録はしない。 

げ。auto って決め打ちじゃないんだ

OSX 10.3.9 + PHP 4.3.11 の環境で

mb_convert_encoding( $str, mb_internal_encoding(), "auto" )

がうまく動かない。何かと思ったら OSX ではこの auto が

"ASCII,JIS,UTF-8,EUC-JP,SJIS"

に展開されず、

"ASCII,UTF-8"

になってしまう。なんじゃこりゃ。てことは明示的に

mb_detect_order()
mbstring.detect_order

とか使って "ASCII,JIS,UTF-8,EUC-JP,SJIS" をセットして auto じゃなくて detect_order を使うようにした方がいいのか。なんでこんなんなってんだ。

[2005-10-28 追記]

[PHP-users 25319] Re: mb_convert_encoding/mb_detect_encoding について

マニュアルの記述と挙動が一致してないらしい。mbstring.language を設定しとけと。なるへそ、チェック不足でした。

[2005-11-10 追記]

と思ったけど、4.3.11 ではエンコーディング判別の処理がバグっている模様。cf. [PHP-users 25298] mb_convert_encoding/mb_detect_encoding について どっちみち 10.3.9 ではダメか…。まぁ他のバージョンを使える環境はいくらでもあるが、どうも PHP ってこういうことが多いよな。

iTrip は意外と使えるのかな

夏に北海道をレンタカーでドライブしたときに初めて利用した iTrip2 なんだけど、このときはホワイトノイズがずっとのっててなんだこりゃとがっかりしていた。(がっかりネタだったので日記には書かなかった。)しかしこの前実家に帰る際に使った1ところ、実にきれいな音。

おや?

何が違うんだろう。機械には強くないからよく分かんねー。とりあえずレンタカーについてたカーナビは切ってみたりしたのでそれの影響は考えにくいんだけど、そもそもカーナビそのものに詳しくないのであんまり自信ない。

チューナーの問題? それともアンプなのかなぁ? レンタカーのときは iPod 本体の音を大きくすると簡単に割れてしまって、しょうがないから iPod 側を小さく、車の方でボリュームを上げるという、どう考えてもノイズばっかでかくなる状態でしか聴けなかった。今回は iPod 側のボリュームを上げても(とりあえずまともに聴ける範囲では)割れないし、調整はかなり自由。

しょぼいラジオと割とゴージャスなラジオで比較すれば、車に載ってる他の要素を排除して比較できるか。でもうちにはまともに鳴るラジオがないorz2

  1. 実家に帰るまでの道のりは県境で確実にラジオが切れる地帯があるので以前から困っていたのだ。 

  2. なんか入りが悪くて放置してる 

メタ議論好きは疲れる

某所でツールの導入について問題となりそうなところとその解決パターンを共有できないかなと呼び水になりそうなメールを投げたのだが、メタ議論好きが釣れてしまった。

組織の分類や状況整理が必要ではないか。文脈がなければ議論として成り立たない。という具合で、それは正しいのだが、じゃあ具体的に自分がフレームを引いてやる、というわけではない。まだこちらの揚げ足取りに終始している。しかし揚げ足取りになっているという自覚はない。

話を進めるというよりは無駄な議論はとっとと収束させてしまえ、という狙いがミエミエなのだが、無視するという行動は取れないらしい。うーん、難しいなぁ。

[追記] うまく落としどころに誘い込めたようだ。うむ、満足。

Mozilla 1.5 印刷よくなってる?

以前、Netscape 7.02 以降の Mozilla Firebird, Mozilla, Netscape について、印刷時に CSS で指定したフォントサイズを無視するって話を書いたのですが、なんかどうも 1.5 で直ってるっぽいです。

よいなぁ。Mozilla.

About

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