2019-10-22

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 とかに限らない普遍的なテクニック 

About

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