ertを使ってelispのunittestを書く方法のメモ
ertを使ってelispのunittestを書く方法のメモ。ertは標準で添付されているのでインストールは不要(なはず)。
使いかた
以下のような関数を定義しておく(テスト対象)
(defun my:add (x y) (+ x y))
テストを書く。
(require 'ert) (ert-deftest add20 () (should (= 20 (my:add 10 10))) )
テストを実行する
- (M-x eval-buffer)
- M-x ert
ertのdefaultのtは全てのテストを実行するという意味。勝手にテストを読み込んでくれないので書いたテストを読み込んであげる必要がある(eval-buffer)。
実行結果は以下のようなもの
Selector: t Passed: 1 Failed: 0 Skipped: 0 Total: 1/1 Started at: 2019-01-07 05:16:12+0900 Finished. Finished at: 2019-01-07 05:16:12+0900 .
失敗した場合
以下のようなテストを追加してみる。これは絶対失敗するもの。
(ert-deftest add21 () (should (= 20 (my:add 10 10))) )
再度ertを実行(M-x ert)
Selector: t Passed: 1 Failed: 1 (1 unexpected) Skipped: 0 Total: 2/2 Started at: 2019-01-07 05:14:03+0900 Finished. Finished at: 2019-01-07 05:14:03+0900 .F F add21 (ert-test-failed ((should (= 21 (my:add 10 10))) :form (= 21 20) :value nil))
テストの書き方
基本的にはert-deftest
のマクロの中で以下を利用してassertionを書く。
- should
- should-not
- should-error
- should-not-err
あと、環境によってスキップしたいものなどは以下を使ってスキップできる。
- skip-unless
例えば、executable-find
でpylosが存在しない場合にスキップしたい場合だとか。
(skip-unless (executable-find "pyls"))
batchで実行(CIなどに)
てきとうにmakefileを書いた。同階層のディレクトリの*test.el
がテストとして認識される。何か依存しているライブラリがあった場合には-l subr
を参考に追加する。
EMACS ?= emacs SELECTOR ?= t TESTS ?= $(wildcard *test.el) test: $(EMACS) -Q --batch -L . \ -l subr \ $(addprefix -l ,$(TESTS)) \ --eval '(setq ert-batch-backtrace-right-margin 100)' \ --eval '(ert-run-tests-batch-and-exit (quote $(SELECTOR)))'
けっこう出力がうるさいのが不便と言えば不便?
$ emacs -Q --batch -L . \ -l subr \ -l 00test.el \ --eval '(setq ert-batch-backtrace-right-margin 100)' \ --eval '(ert-run-tests-batch-and-exit (quote t))' Running 2 tests (2019-01-07 05:22:33+0900) passed 1/2 add20 Test add21 backtrace: signal(ert-test-failed (((should (= 21 (my:add 10 10))) :form (= 21 20) :value nil))) ert-fail(((should (= 21 (my:add 10 10))) :form (= 21 20) :value nil)) (if (unwind-protect (setq value-7 (apply fn-5 args-6)) (setq form-description-9 (nconc (list '(sho (let (form-description-9) (if (unwind-protect (setq value-7 (apply fn-5 args-6)) (setq form-descri (let ((value-7 'ert-form-evaluation-aborted-8)) (let (form-description-9) (if (unwind-protect (set (let* ((fn-5 (function =)) (args-6 (condition-case err (let ((signal-hook-function (function ert-- (lambda nil (let* ((fn-5 (function =)) (args-6 (condition-case err (let ((signal-hook-function (fu ert--run-test-internal(#s(ert--test-execution-info :test #s(ert-test :name add21 :documentation ni ert-run-test(#s(ert-test :name add21 :documentation nil :body (lambda nil (let* ((fn-5 (function = ert-run-or-rerun-test(#s(ert--stats :selector t :tests [#s(ert-test :name add20 :documentation nil ert-run-tests(t #f(compiled-function (event-type &rest event-args) #<bytecode 0x4883c9>) nil) ert-run-tests-batch(t) ert-run-tests-batch-and-exit(t) eval((ert-run-tests-batch-and-exit 't)) command-line-1(("-L" "." "-l" "subr" "-l" "00test.el" "--eval" "(setq ert-batch-backtrace-right-ma command-line() normal-top-level() Test add21 condition: (ert-test-failed ((should (= 21 (my:add 10 10))) :form (= 21 20) :value nil)) FAILED 2/2 add21 Ran 2 tests, 1 results as expected, 1 unexpected (2019-01-07 05:22:33+0900) 1 unexpected results: FAILED add21