👻

Serverspecを読めるようになるための最低限の文法まとめ

に公開

はじめに

インフラの単体テストで ServerSpec が使用されることがある。

ServerspecはRubyのテストフレームワークRSpecを拡張して作られておりdescribe, it, should, each などの構文は RubyやRSpecに由来 している。

本記事では、Serverspecを理解するために最低限の文法を整理した。


1. describe と it

describe service('nginx') do
  it { should be_running }
end
構文 意味
describe テスト対象(文脈)を定義
it 期待する状態を定義
should 「〜であるべき」という期待

「describeで文脈を作り、itで期待を書く」 という設計思想を採用している。

参考: RSpec公式 – Describe & It


2. should / should_not:期待値の評価

it { should be_enabled }
it { should_not be_disabled }

should / should_not は真偽値(True/False)を評価するために使う。

参考: RSpec Expectations


3. each:ループ

複数のパッケージやサービスをまとめてテストしたいとき、Rubyのeachを利用して動的に記述できる。

%w(httpd git curl).each do |pkg|
  describe package(pkg) do
    it { should be_installed }
  end
end
構文 意味
%w() 文字列配列リテラル(例:["httpd", "git", "curl"]
.each 配列の要素を1つずつ取り出して繰り返す
do ... end 繰り返し処理のブロック構文
pkg ループ内で使う変数(各要素が順に入る)

動作の流れ:

  1. %w(httpd git curl) により3要素の配列を生成
  2. .each により1要素ずつ取り出す
  3. 各要素が pkg に渡され、describe package(pkg) が評価される
  4. end でブロックが閉じられる
  • 複雑な構成では each をネストして複数ロールや環境変数を繰り返すケースもある。

4. let と $変数名:変数

ServerspecはRubyで書かれているため、Rubyの変数やRSpec特有の変数スコープ管理が利用できる。
代表的なものが グローバル変数 $namelet構文

# spec_helper.rb
$server_ip = "192.168.10.10"

# specファイル
describe interface('eth0') do
  it { should have_ipv4_address("#{$server_ip}/24") }
end

上記のように $server_ip を使えば、複数ファイル間で共通の値を利用できる。
テストファイル内限定の変数を let で定義することができる。

let(:port) { 80 }

describe port(port) do
  it { should be_listening }
end
種類 スコープ 用途
$global 全ファイル共通 設定値(例:hosts.ymlなどから読み込む)
let(:name) ファイル内限定 一時的・テスト専用の値定義

let は「呼び出されたタイミングで初めて実行される遅延評価」であり、
RSpecのテストをより安全に保つための仕組みである。

参考: RSpec let


5. shared_examples:共通テストの再利用

複数のサーバやロールで共通化できるテストを一箇所にまとめて再利用できる。
コードの重複を減らすことが目的。

# shared/packages.rb
shared_examples 'package check' do |packages|
  packages.each do |pkg|
    describe package(pkg) do
      it { should be_installed }
    end
  end
end

# web_spec.rb
include_examples 'package check', %w(httpd git curl)

shared_examples で定義したブロックを
include_examples で読み込むだけで、複数環境に同じテストを適用できる。

参考: RSpec shared_examples


6. command と its(:stdout)

describe command('curl -I localhost') do
  its(:stdout) { should match /200 OK/ }
end

describe command() はシェルコマンドを実行するテスト。
出力結果を its(:stdout) で検証する。

構文 意味
command() 実際にコマンドを実行
its(:stdout) 標準出力の内容を検証対象にする
match /regex/ 正規表現で評価する

参考: Serverspec command resource


7. context:条件別のdescribe

context 'on CentOS' do
  describe package('httpd') do
    it { should be_installed }
  end
end

contextdescribe の別名だが、「条件(環境・設定)」を明示するために使われる。

参考: RSpec context


8. if:条件分岐

Rubyのif構文をそのまま利用できる。
OSや条件によって異なる検証をしたい場合などに使う。

service_name = os[:family] == 'centos' ? 'httpd' : 'nginx'

describe service(service_name) do
  it { should be_running }
end

ブロック内で条件分岐をすることも可能:

%w(httpd nginx).each do |svc|
  describe service(svc) do
    it { should be_running } if svc == 'nginx'
  end
end
構文 意味
if 条件 条件が真なら実行
unless 条件 条件が偽なら実行
? : 三項演算子

9. its と match / include

設定ファイルなどの内容を検証する際に使う。
its(:content) はファイルの内容を取得し、
matchinclude で文字列・正規表現で確認する。

describe file('/etc/nginx/nginx.conf') do
  its(:content) { should match /worker_processes/ }
  its(:content) { should include "nginx" }
end
構文 検証内容
match /regex/ 正規表現に一致すること
include "文字列" 指定文字列を含むこと

Discussion