Clojureのテストリソース管理ツール「cavia」
はじめに
caviaはClojureプロジェクトでリモートのテスト用リソースを扱うためのライブラリである。
しばしば、大きなサイズのファイルがテストに必要なときがある。たとえば、パーサーのプログラムのサンプルファイルや、画像処理におけるテスト画像などが考えられる。しかし、一般的に大サイズファイルやバイナリファイルをGitなどのSCMツールで管理するのは適切でない。それらをうまく管理するためにgit-annexやGit LFSといった外部ツールも存在するが、Clojureのエコシステムの中で解決したいケースのためにcaviaは作られた。
caviaは、リモートのリソースを自動的にダウンロードし、ハッシュをチェックしてくれる。また、簡単にリソースにアクセスする関数を提供してくれる。caviaを使うことで、いつものようにlein test
を実行するだけでテスト用のリソースを使ってテストできる。
使い方
インストール
Clojarsで配布されているため、LeiningenやBootの依存設定に以下を追加する。
[cavia "0.4.1"]
基本的にテスト時にしか使わないので、:profiles :dev :dependencies
に設定しておくのがよい。
プロファイルの定義
まず、リモートからダウンロードしたいリソース群を定義する。プロファイルの定義にはdefprofile
を用いる。
(require '[cavia.core :as cavia :refer [defprofile]])
(defprofile prof
{:resources [;; 単純なHTTP
{:id :resource1
:url "http://example.com/resource1"
:sha256 "0123456789abcdef01234567890abcdef01234567890abcdef01234567890abc"}
;; ベーシック認証
{:id :resource2
:url "http://example.com/resource2"
:sha1 "123456789abcdef01234567890abcdef01234567"
:auth {:type :basic, :user "user", :password "password"}}
;; FTP
{:id :resource3
:url "ftp://example.com/resource3"
:sha256 "23456789abcdef01234567890abcdef01234567890abcdef01234567890abcde"
:auth {:user "user", :password "password"}}
;; 圧縮されたファイル
{:id :resource4
:url "http://example.com/resource4.gz"
:sha1 "3456789abcdef01234567890abcdef0123456789"
:packed :gzip}]
:download-to ".cavia"})
:resources
にダウンロードするリソースの情報を記述する。
必須:
:id
- リソースファイルへのアクセス等に用いる。KeywordあるいはStringが使用できる。:url
- リソースのリモートURL。HTTP/HTTPS/FTP/FTPSに対応している。:md5/:sha1/:sha256
- リソースファイルのチェックに用いるハッシュ。MD5/SHA1/SHA256に対応している。複数指定されている場合、最も強いハッシュアルゴリズムが利用される。
オプショナル:
:auth
- ベーシック認証あるいはダイジェスト認証の情報を記述する。:packed
- 圧縮されたファイルをダウンロード後に自動的に展開する。現在のところgzip (:gzip
) のみ対応している。
:download-to
にはリソースを配置するローカルのディレクトリを指定する。デフォルトでは./.cavia
に保存される。
リソースのマネジメント
(cavia/get! prof)
- まだダウンロードされていないファイルをダウンロードする。(cavia/verify prof)
- ダウンロード済のリソースのハッシュをチェックする。(cavia/clean! prof)
- ダウンロード済のリソースを削除する。
いちいちプロファイルを指定するのが面倒であれば、with-profile
マクロを用いるのがよい。
(with-profile prof
(cavia/clean!)
(cavia/get!))
ダウンロードの進捗などが標準出力に表示されるが、もし非表示にしたければwithout-print
マクロを用いる。
(without-print
(cavia/get! prof))
リソースへのアクセス
recource
関数にプロファイルに記述した:id
を渡せば、そのリソースへの絶対パスを取得できる。存在しないIDを渡した場合はnil
が返ってくる。
(cavia/resource prof :resource1)
;;=> "/path/to/.cavia/resource1"
(cavia/resource prof :undefined)
;;=> nil
テストフレームワークとの組み合わせ例
clojure.test
clojure.testと組み合わせる場合は、次のようにfixture関数を作って、その中でget!
を呼び出すことで、各テストケースの実行前にリソースをダウンロードできる。
(ns foo.core-test
(:require [clojure.test :refer :all]
[cavia.core :as cavia :refer [defprofile]]))
(defprofile prof
{:resources [{:id :resource1
:url "http://example.com/resource1"
:sha256 "0123456789abcdef01234567890abcdef01234567890abcdef01234567890abc"}]})
(defn fixture-cavia [f]
(cavia/get! prof)
(f))
(use-fixtures :once fixture-cavia)
(deftest your-test
(testing "tests with the cavia's resource"
(is (= (slurp (cavia/resource prof :resource1)) "resource1's content")))
Midje
Midjeと組み合わせる場合は、with-state-changes
を用いて、fact前に呼び出すのがよい。
(ns foo.t-core
(:require [midje.sweet :refer :all]
[cavia.core :as cavia :refer [defprofile with-profile]]))
(defprofile prof
{:resources [{:id :resource1
:url "http://example.com/resource1"
:sha256 "0123456789abcdef01234567890abcdef01234567890abcdef01234567890abc"}]})
(with-profile prof
(with-state-changes [(before :facts (cavia/get!))]
(fact "tests for a large file" :slow
(slurp (cavia/resource :resource1) => "resource1's content")))
)
おわりに
caviaが便利だと思えるケースはそんなにないかもしれないが、Clojure関連以外の余計な操作を必要としない点で有用だと考えている。筆者の場合は、Git LFS等でも扱いが難しい数十GB以上のテストリソースを用意するのに重宝している。