serverspecを環境にあわせてカスタマイズ

| トラックバック(0)

serverspecをより柔軟に活用するためには、独自の環境にあわせてカスタマイズして利用することが必要な場面があります。本記事では、そのような場面での対応方法をいくつか紹介します。

前回の記事 で、テストコードを書いてテストを実行するところまでは実施できるようになりました。また、前回の記事で、SSH接続時の処理内容を変更したい場合にはspec_helper.rbをカスタマイズして対応が可能といったことを紹介しましたが、今回は実際のテストの実行内容に関してカスタマイズする方法について紹介します。

serverspecのResourceTypeやMatcherは非常に豊富に実装されているためこれらを活用することで非常に多くのテストコードが作成できます。

しかし、中には、標準で用意されているResourceTypeやMatcherだけではカバーしきれない場合もあります。serverspecはそういった場面でも柔軟に対応できるような作りになっています。カスタマイズの方法は様々ありますが、本記事では以下の3つの方法について紹介します。

  • ResourceType "command"を使うことによる任意のコマンドのテスト実行の実現
  • MatcherをカスタマイズすることによるResourceTypeに対するテスト実行の拡張
  • カスタマイズした新たなResourceTypeを追加することによるテスト可能な範囲の拡張

ResourceType "command"

serverspecにはcommandというResourceTypeが用意されています。これは、任意のコマンドの実行結果をテストするためのResourceTypeです。システム固有のアプリケーションの状況をテストしたい場合など、標準のResourceTypeとしては用意されていないがテストしたいといった場合に便利です。

具体例

例えば、MySQLのデータベースの文字コードの設定をテストしたいといった場合、mysqladminコマンドを使うと以下のように確認できます。

$ mysqladmin variables | grep character_set_database | awk -F "|" '{print $3}'
utf8

これをResourceTypeのcommandを利用することで以下のようにテストコードを記述することができます。

mysql_sepc.rbというファイルを作成し以下を記述します。

require 'spec_helper'

describe command("mysqladmin variables | grep character_set_database | awk -F \"|\" '{print $3}'") do
  it { should return_stdout /utf8/ }
end

今回は、標準出力の結果をテストしたいのでMatcherとしてはreturn_stdoutを利用しました。これでMySQLのデータベースの文字コード設定がutf8であることを自動的にテストすることができます。
このように環境独自のテストを簡単にテストコードに落としこむことができます。

カスタムMatcher

既に用意されているResourceTypeに対して、独自のMatcherを作ってテストしたい場合には、Matcherを追加で実装します。テスト対象のリソースは既にResourceTypeで定義されているが、テストしたい内容が既存のMatcherでは実現できない場合等にこの方法で拡張することができます。

具体例

例えば、 前回の記事 で紹介したfileというResourceTypeに対して、ファイルサイズをテストするための実装を追加してみます。RSpecには値を比較するための"should eq"や"should be <" "should be >"などのMatcherが既に実装されているため、ファイルサイズを取得するメソッドのみを追加実装すればOKです。
serverspecはRubyで実装されているため、Rubyのオープンクラスの特性( 参考 )を利用し、以下のように実装してみます。

前回の記事で紹介したfile_spec.rbを以下のように変更します。

require 'spec_helper'

module Serverspec
  module Type
    class File < Base
      def size
        ret = backend.run_command(commands.check_file_size(@name))
        val = ret.stdout.strip
        val = val.to_i if val.match(/^\d+$/)
        val
      end
    end
  end
end

module SpecInfra
  module Command
    class Base
      def check_file_size(file)
        "du --bytes #{escape(file)} | awk '{print $1}'"
      end
    end
  end
end

describe file('/home/ikeda/test.conf') do
  it { should be_owned_by 'ikeda' }
  it { should be_mode 600 }
  it { should contain('Allow from all').from(/^<Directory \"\/var\/www\/html\/test\">/).to(/^<\/Directory>/) }
  its(:size) { should be < 1024 }
end

ResourceType "file"に対してファイルサイズを返すメソッド(size)を新たに実装し、バックエンドでduコマンドを使ってファイルサイズを取得するようにしています。
テストコードとしては、its(:size) { should be < 1024 }という記述を追加することでテスト対象のファイル(/home/ikeda/test.conf)が1024bytesよりも小さいかどうかをテストすることができます。

カスタムResourceType

最後に、その他のカスタマイズ方法として、あらたにResourceTypeを定義する方法があります。標準では存在しないリソースに対するテストを定義することができるため、serverspecでテストできる範囲を拡張することに繋がります。
先ほどのMySQLのステータスのテストなどある程度、リソースに対して様々なテスト項目が考えられるような場合や繰り返し利用されるような場合についてはResourceTypeとして定義しておくと便利に活用できます。

ResourceTypeを追加するには、以下の追加実装が必要です。

  1. lib/serverspec/type以下に新たなResourceType用クラスを実装
  2. lib/helper/type.rbに新たなResourceTypeの名前を登録

具体例

先ほどのMySQLのステータス情報をテストするResourceTypeを新たに作成してみます。

lib/serverspec/typeに新規クラス実装

lib/serverspec/type/mysql.rbを以下の内容で作成します。

module Serverspec
  module Type
    class Mysql < Base
      def method_missing(meth)
        ret = backend.run_command(commands.check_mysql_info(@name, meth))
        val = ret.stdout.strip
        val
      end
    end
  end
end

module SpecInfra
  module Command
    class Base
      def check_mysql_info(command, column)
        "mysqladmin #{command}| grep #{column} |awk -F \"|\" '{print $3}'"
      end
    end
  end
end

lib/helper/type.rbに新たに追加したmysqlというタイプを登録

module Serverspec
  module Helper
    module Type
      types = %w(
        base cgroup command cron default_gateway file group host iis_website iis_app_pool interface
        ipfilter ipnat iptables kernel_module linux_kernel_parameter lxc mail_alias
        package php_config port process routing_table selinux service user yumrepo
        windows_feature windows_registry_key zfs mysql ←mysqlを追加
      )
    ・・・以下略

テストコード

これだけの追加実装をすることで、以下のようなテストコードでMySQLの変数情報のテストができるようになります。

describe mysql('variables') do
  its(:character_set_database) { should eq "utf8" }
end 

まとめ

前回の基礎編に引き続き、よりさまざまな場面で適用できるようserverspecのテスト実行内容をカスタマイズする方法を紹介しました。Rubyで実装されているということも相まって、後からの追加実装が非常に簡単にできるようになっています。このような方法を知っておくことで、より有効に活用できるのではないでしょうか。

serverspecは非常に早いペースで開発が継続されています。そのため、カスタマイズする場合にはserverspecのバージョンアップには注意が必要です。進化途上のソフトウェアであるため、開発の状況等ウォッチしながら、serverspecの標準でできることできないことを認識した上で対策をとることをおすすめします。
また、多くの人が活用しそうなカスタマイズ内容であれば、serverspecの標準に組み込まれると幸せになるかもしれません。そういった場合には開発者にコンタクトを取ってみるのもひとつの手段かと思います。

トラックバック(0)

トラックバックURL: http://tech-sketch.jp/mt-tb.cgi/199

このブログ記事について

このページは、池田 大輔が2014年5月26日 10:00に書いたブログ記事です。

ひとつ前のブログ記事は「Scala/Play2でWebアプリケーション開発~(4)SlickコードジェネレータとTypesafe Activatorの利用」です。

次のブログ記事は「書籍「Chef活用ガイド」を頂いたので読みました」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ