宇宙は究極のフリーランチ

マネジメント / 意思決定 / プログラミング

アプリケーションエンジニアが初めてserverspecに触ってみた

テストコードによるインフラのテスト

serverspecはインフラの構築状況に対してテストコードによるテストを行うgemだ。
packageのインストール状況、serviceの起動状況、ポートの開放状況などの各種設定状況を、 rspecの文法でテストできる

f:id:Shinya_131:20150301212949p:plain

このserverspecと、サーバー構築自動化ツール(itamaeとか)を組み合わせて 環境構築をテストファーストで行う TDI( = Test Driven Infrastructure = テスト駆動インフラ)などのコンセプトが登場しており大変興味深い。

個人の開発環境整備に使えないか

もちろんserverspecを使って個人の開発環境をテストする事が可能なようだ。

今回は、実現したい明確な目的があるわけでは無いが、 とりあえずこんな事に役立つかどうか知るために試しに触ってみた。

自身の開発環境構築

  • 自分の開発環境をコマンド1つで、0から自動で構築できたら便利そう。
  • まずは、正しく構築できたか?serverspecでテストしてみたい。

チームメンバーの開発環境構築

インストール

gem install serverspec

init

serverspec-initを実行するとテンプレートファイルを生成してくれる。

$ serverspec-init
Select OS type:

1) UN*X
2) Windows

Select number: 1

Select a backend type:

1) SSH
2) Exec (local)

Select number: 2

+ spec/
+ spec/localhost/
+ spec/localhost/sample_spec.rb
+ spec/spec_helper.rb
+ Rakefile
+ .rspec
  • sshを選ぶとリモートホストに対してテストが行えるんだろう。今回はlocalを選択。

テストコード

  • 書く。
# spec/localhost/sample_spec.rb
require 'spec_helper'

# middleware
describe package("mysql") do
  it { should be_installed }
end

describe package("redis") do
  it { should be_installed }
end

describe package('rbenv') do
  it { should be_installed }
end

describe package('rails') do
  it { should be_installed.by('gem') }
end

# VCS
describe package('git') do
  it { should be_installed }
end

describe package('tig') do
  it { should be_installed }
end

# development tools
describe package('pry') do
  it { should be_installed.by('gem') }
end

describe command "ls /Applications | grep -q Atom" do
  its(:exit_status) { should eq 0 }
end

describe command "ls /Applications | grep -q Xcode" do
  its(:exit_status) { should eq 0 }
end

実行

$ rake
/Users/nagai_shinya/.rbenv/versions/2.1.0/bin/ruby -I/Users/nagai_shinya/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/rspec-support-3.2.1/lib:/Users/nagai_shinya/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/rspec-core-3.2.0/lib /Users/nagai_shinya/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/rspec-core-3.2.0/exe/rspec --pattern spec/localhost/\*_spec.rb

Package "mysql"
should be installed

Package "redis"
should be installed

Package "rbenv"
should be installed

Package "rails"
should be installed

Package "git"
should be installed

Package "tig"
should be installed

Package "pry"
should be installed

Command "ls /Applications | grep -q Atom"
exit_status
should eq 0

Command "ls /Applications | grep -q Xcode"
exit_status
should eq 0

Finished in 2.47 seconds (files took 1.06 seconds to load)
9 examples, 0 failures

感想

  • 便利そう。
  • とりあえず各種packageのインストールが簡単にテスト出来た。
  • インフラはアプリケーションに比べて内部状態が少ないし、 外部APIが絡むケースも少ないので、シンプルなテストコードだけで済む場合が多い印象。
    • MockとかStubとか使わなくて良さそう。
    • #setupすらあまり使わないかも。
    • テストファーストの入門としては、TDDよりTDIの方が入りやすいかもしれない。

あとで調べる

  • インストールされたpackageのバージョンをテストする方法。
  • Applicationsへのインストールをテストする方法。
    • custom matcherを書けば良さそう。

実際どういうコマンドが実行されているのか気になる。

  • 失敗するテストを書けば分かるという気付きがあった。

例:

存在しないpackage名を指定してみる
describe package('pryyyyyyy') do
  it { should be_installed.by('gem') }
end
結果:
Failures:

1) Package "pryyyyyyy" should be installed
On host `localhost'
Failure/Error: it { should be_installed.by('gem') }
expected Package "pryyyyyyy" to be installed
/bin/sh -c gem\ list\ --local\ \|\ grep\ -iw\ --\ \\\^pryyyyyyy

# ./spec/localhost/sample_spec.rb:31:in `block (2 levels) in <top (required)>'

/bin/sh -c gem\ list\ --local\ \|\ grep\ -iw\ --\ \\\^pryyyyyyy というコードでテストを行っている事が分かる。

今日はここまで。