2008-04-10
■[emacs][el-mock]elisp用stub/mockフレームワーク作成中
elisp用のmockやstubのフレームワークってないの? - (rubikitch loves (Emacs Ruby CUI Books))
ないようなので作成中。名前はel-mock.elだ。まだ途中なので経過をブログに晒しておく。
(eval-when-compile (require 'cl))
(defun with-stub-function (stub-spec body-func)
(loop for (funcsym _ value) on stub-spec
when (eq _ '=>)
collecting (progn
(condition-case e (ad-deactivate funcsym) (error nil))
(list funcsym value)) into pair
finally
(return
(unwind-protect
(eval `(flet (,@(loop for (funcsym value) in pair
collecting
`(,funcsym (&rest x) ,value)))
(funcall body-func)))
(loop for (funcsym value) in pair
do (condition-case e (ad-activate funcsym) (error nil)))))))
(defmacro with-stub (stub-spec &rest body)
`(with-stub-function ',stub-spec (lambda () ,@body)))
(put 'with-stub 'lisp-indent-function 1)
特定の範囲のみで関数を偽装するwith-stubマクロ。偽装された関数は、どんな引数で呼ばれても常に同じ返り値を返す。fletでも偽装できるが、adviceを考慮してないのでスタブとしてはうまく機能しない。
(with-stub (偽装する関数名1 => 偽装関数の返り値1
偽装する関数名2 => 偽装関数の返り値2
...)
式...)
な感じで記述する。「=>」は趣味。
たとえば、set-window-start等「画面」に依存する関数を偽装することで、通常ならば目視確認しかできなかったテストが自動化できるようになる…はず。
el-expectations.elによるユニットテストはこんなの。
(expectations
(desc "with-stub")
(expect 2
(with-stub (foo => 2)
(foo 1 2 3)))
(expect 3
(defun defined-func (x) 3)
(unwind-protect
(with-stub (defined-func => 3)
(defined-func 3))
(fmakunbound 'defined-func)))
(expect "xx"
(with-stub (find-file => "xx")
(find-file "xx")))
(expect 5
(with-stub (a => 3 b => 2)
(+ (a 999) (b 7)))))
モックについては、引数と返り値のverifyを行うようにする。しかし、どのようなDSLにするかは決めかねている。迷う。
