7/14/2017
このエントリーをはてなブックマークに追加

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以上のテストリソースを用意するのに重宝している。

Tags: Clojure