EmacsLispで最小構成(っぽい)テストをする方法

ERTでテストをするやり方が気になったので、Githubでテストしているリポジトリをいくつか見ていった。

いくつかのリポジトリを見て、これぐらいなら後々応用が利くかなと程度のものができたので記事にしてみようと思う。

テストはEmacs24でついてくるようになったERTをMakefileでテストするやつになる。

https://github.com/pogin503/emacs-test-sample

ここに作ったやつが置いてあるので

$ git clone git://github.com/pogin503/emacs-test-sample.git
$ cd emacs-test-sample
$ make test

で実行出来ます。

実行例

$ make test
/Applications/Emacs.app/Contents/MacOS/Emacs --batch -Q -L . --eval \
		"(progn \
		(setq byte-compile-error-on-warn t) \
		(batch-byte-compile))" *.el
Wrote /Users/username/repo/emacs-test-sample/myfile.elc
/Applications/Emacs.app/Contents/MacOS/Emacs --batch -Q -L . -l test/run-test.el
Running tests on Emacs 24.3.1
Loading /Users/username/repo/emacs-test-sample/test/myfile-test.el (source)...
Running 1 tests (2013-06-18 01:57:09+0900)
   passed  1/1  myfile:addition-test

Ran 1 tests, 1 results as expected (2013-06-18 01:57:09+0900)

中身について

フォルダ構成

フォルダ構成はこんなふうにしている。

.
├── Makefile
├── myfile.el
└── test
    ├── lib      #テスト用のライブラリディレクトリ(必要があれば作る)
    │   ├── cl-lib.el
    │   └── ert.el
    ├── myfile-test.el  #実行されるテスト
    └── run-test.el     #実行用の設定をしてテストを走らせるファイル
  • test用の実行関数などはmyfile-test.elに入れる。実際に実行させるのはrun-test.elに実行させるのが良いと思う。
  • libの中には古いEmacs用にert.elを入れたり、他の必要なライブラリを入れておけばいい。
  • libの位置はトップディレクトリにあるべきか、test内に入れたほうがいいのかわからない。自分はtestの中に入れとくのがいいのかなと思う。
バッチ処理

Emacsバッチ処理は-batch または--batchをオプションに入れて実行します。

$ emacs --batch -L . --eval "(message \"Running tests on Emacs %s\" emacs-version)"

バッチ処理でよく使うオプションはこんなの

オプション 効果
-Q .emacsや、.emacs.d/init.elを読み込まないようにする、かつ最小の設定で起動する
-L directory directoryをload-pathに追加する
-f function functionを引数なしで読み込む
-l file ファイル名をloadする
--batch バッチモードを動かす
--eval expression --evalの後に続くLispプログラムを評価する
Makefile

Makefileはこんな風にすればいいんじゃないかと思う。

# Makefile

UNAME:=$(shell uname -s)

#多分動くと思う
ifeq ($(UNAME),Darwin)
EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
else
EMACS=emacs
endif

.PHONY : build test

build :
	$(EMACS) --batch -Q -L . --eval \
		"(progn \
		(setq byte-compile-error-on-warn t) \
		(batch-byte-compile))" *.el

test: build
	$(EMACS) --batch -Q -L . -l test/run-test.el

Macを使っていると/usr/binにある古いEmacsが使われるので

ifeq ($(UNAME),Darwin)
EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
else
EMACS=emacs
endif

ifeq普段使うEmacsを設定しておかないといけない。

  • build部分ではバイトコンパイルをしています。ロードするファイルが多くなれば多分体感できるぐらいまで早くなるのかも。別になくてもいい気がする。

だいたいこんな風にすれば最小限の設定で動かすことができてテストをすることができます。

myfile.el

ERTで実行する用に用意する。

;; myfile.el
(defun myfile:addition (a b)
  (+ a b))

(provide 'myfile)
test/myfile-test.el

実行されるファイルには(require 'ert)、(require 'myfile)とかして必要なファイルをものをロードする。

ERTはert-deftestを使って実行するテストを作る。

;; test/myfile-test.el
(require 'ert)

(require 'myfile)

(ert-deftest myfile:addition-test ()
  (should (= (myfile:addtion 1 2) 3)))
test/run-test.el

このファイルで実際にテストをするためのロードパスや、読み込むライブラリを設定する。

Makefileにの-lオプションで読み込むときに使われる。

少々ながいのでgithubの方で見てください。

https://github.com/pogin503/emacs-test-sample/blob/master/test/run-test.el

test/run-test.el 内でテストを実行してる部分は下の部分になる。

;; test/run-test.el

;; Run tests
(if noninteractive
    (ert-run-tests-batch-and-exit)
  (ert t))

テストをしていたリポジトリ

GitHub - uk-ar/el-spec
GitHub - bbatsov/projectile: Project Interaction Library for Emacs
GitHub - rolandwalker/emacs-travis: Travis CI recipe for Emacs libraries.
GitHub - ecukes/ecukes: Cucumber for Emacs
GitHub - cask/cask: Project management tool for Emacs
GitHub - auto-complete/auto-complete: Emacs auto-complete package
GitHub - emacs-mirror/emacs: Mirror of GNU Emacs
GitHub - tkf/emacs-jedi: Python auto-completion for Emacs
GitHub - haskell/haskell-mode: Emacs mode for Haskell
https://github.com/flycheck/flycheck

travis-ciでテストをする場合は rolandwalker/emacs-travis を見ればいいと思う。でもMakefileの中身でいろいろやってて複雑ではある。

やっぱりこっちの方が簡単そうなのでこっちを見るといいと思う。
GitHub - lewang/ert-test-skeleton

(追記 2013.6.18)
Cartonを使ったtravis-ciのテストをtkfさんが作ってくれたのでそれも紹介する
GitHub - tkf/emacs-plugin-template: Minimal emacs plug-in template with setup for Travis CI

Cartonを使うほうがだいぶ楽そう。

追記2013/10/3
→CartonからCaskに名前が変わった。

終わりに

とりあえずERTのバッチテストは出来たのでひとまずは満足。

そしてこの記事を書いた後にもっと良さげなサンプルを見つけてしまうという...。

自分はまだテストちゃんと作ったことが無いのでちゃんと作ってみようと思う。

(追記 2013.6.18)
el-specとかEcukesとかERTをカバーする実装があるので、そっちの導入を次は書けばいいのかなと。