シェルスクリプトのテスト

以前シェルスクリプトのテストについて少し書きました。

シェルスクリプトの開発環境 - wyukawa’s blog

最近実際にテストを始めたのでどうやっているかを書いてみたいと思います。

やりたいことはHiveQLをキックするシェルスクリプトのテストです。

ブラックボックステストでテストの粒度はSIerでいう?単体をイメージしてます。

1ファイルのシェルスクリプトをテストするシェルスクリプトは1ファイルでその中にテストメソッドが観点ごとに複数存在するイメージです。

またブラックボックステストなのでシェルスクリプト内の分岐を網羅するようなテストは想定していません。もっと粒度は大きいです。そうじゃないとメンテナンス大変だし。

テスティングフレームワークshunit2を使います。

テストコードのディレクトリ構成はこんな感じです。テストケースごとにテストデータ、テスト結果置き場、期待値データがあります。

    example_test.sh テストコード
    input/ テストデータ置き場
      001/ テストケースID
        input.dat テストデータ
    output/ テスト結果の置き場
      001/ テストケースID
    expected/ テスト結果の期待値データ置き場
      001/ テストケースID
        expected.dat 期待値データ

テストケースごとにテストメソッドを作ります。入出力データはファイルだったりテーブルの中身だったりです。

テストのバリエーションは入出力データのバリエーションでもあり、テストメソッドは共通化します。

テストコードはこんな感じ。

#! /bin/sh

#前処理
BASE_HOME=$(cd $(dirname $0); cd ..; pwd)

#共通メソッド
common()
{
  id=$1 #テストケースID
 input=${BASE_HOME}/.../input/${id}/input.dat #入力データ
 outputdir=${BASE_HOME}/.../output/${id}
  .../example.sh ${input} #プロダクトコード実行。たとえばテーブルへのinsert
  exit_code=$?
  assertEquals 0 ${exit_code} # assert実行

  hive -f result.hql > ${outputdir}/output.dat #プロダクトコード実行結果を取得。たとえばテーブルのselect

  expected=${BASE_HOME}/.../expected/${id}/expected.dat
  diff ${expected} ${outputdir}/output.dat #期待値と実際の結果を比較
  exit_code=$?
  assertEquals 0 ${exit_code}

}

test001()
{
  common 001
}

test002()
{
  common 002
}

test003()
{
  common 003
}

# load shunit2
. .../src/shell/shunit2

ちなみにHiveを実行するので1つのテストメソッドの実行にもそれなりに時間かかります。

このままだとtest003だけを実行したいのにtest001もtest002も実行することになります。

実行したいテストメソッド以外をコメントアウトするでもいいのですが、suite_addTestを使うとそこそこ幸せになれます。

suite_addTestはsuiteから呼び出されるもので実行するテストメソッドを指定します。

なのでtest003に注力している場合は下記のように書けばtest003のみ実行されます。

test001()
{
  common 001
}

test002()
{
  common 002
}

test003()
{
  common 003
}

suite()
{
  suite_addTest test003
}

test003がうまくいってtest004を実行したい場合は下記のようにします。

test001()
{
  common 001
}

test002()
{
  common 002
}

test003()
{
  common 003
}

test004()
{
  common 004
}

suite()
{
  suite_addTest test004
}

最後のテストメソッドの確認が終わったら下記のようにsuiteの部分をコメントアウトします。そうすればすべてのテストメソッドが実行されるようになります。

test001()
{
  common 001
}

test002()
{
  common 002
}

test003()
{
  common 003
}

test004()
{
  common 004
}

...

test010()
{
  common 010
}

#suite()
#{
#  suite_addTest test010
#}

ちなみに1つのシェルスクリプトで多くのことをやりすぎている場合は分割します。そのほうがテストもしやすいです。

またA,BとシェルスクリプトがあってBを実行するにはAを実行しなければならないようなケースだとさらにスローテストな感じです。


ま、こんな感じです。