新年はげましておめでとうございます。

はじめに

  • ずっとやらなきゃって思って引きずってきた Rspec をやってみる
  • ちゃんとうんちくを押さえておきたいけど、まずは写経したりサンプルを弄ったりしながら
  • 合わせて Rspec を Travis CI で実行させてみる

以下、色々と試しながら書いているので間違い等があると思われるので、適宜、アップデートしていく。

参考

## はじめにRSpecは難しい、よくわからない、といったコメントをときどき見かけます。確かにちょっと独特な構文を持っていますし、機能も結構多いので「難しそう」と感じてしまう気持ちもわかります。(構文については僕も最初見たと...

qiita.com

## はじめにみなさんこんにちは!この記事は「必要最小限の努力で最大限実戦で使える知識を提供するRSpec入門記事」、略して「使えるRSpec入門」の第2回です。今回はRSpecのマッチャについて説明していきます。第1回...

qiita.com

## はじめにみなさんこんにちは!この記事は「必要最小限の努力で最大限実戦で使える知識を提供するRSpec入門記事」、略して「使えるRSpec入門」の第3回です。今回はRSpecのモックを使ったテストについて説明します。...

qiita.com

ユニットテストで使われるモックとスタブを正しく理解するために、シンプルな説明を作ってみました。モックとスタブを有効に活用して、より効率的なTDDを目指します! (adsbygoogle = window.adsbygoogle || ).push({}); スタブ/モックとは? スタブ/ …

morizyun.github.io

上記記事がとても参考になった。有難うございます。今後の為にもリンクを貼っておく。

Hello Rspec

何はともあれ

うんちくは後回しにしてサンプルスクリプトを使って Rspec をざっくりと体験してみる。

サンプル Ruby スクリプト

以下のようなスクリプトを zura.rb という名前で用意する。

class Zura
  def hage(str)
    str
  end
end

watashi = Zura.new()
p watashi.hage("私はズラではない")

実行すると以下のような結果が得られる。

$ ruby zura.rb
"私はズラではない"

本当にズラではありません。

テスト

テストと称してして zura_spec.rb を以下のように作成。

require_relative 'zura'

describe Zura, "のテスト" do

  before do
    @watashi = Zura.new()
  end

  context '#hage の引数が "私はズラ" の場合' do
    before do
      @arg = "私はズラ"
    end
    it '私はズラ' do
      expect(@watashi.hage(@arg)).to eq '私はズラ'
    end
  end

  context '#hage の引数が指定されていない場合' do
    it '引数エラーとなる' do
      expect{@watashi.hage}.to raise_error(ArgumentError)
    end
  end

end

テスト実行

以下のようにテストを実行する。

$ rspec --color -fd zura_spec.rb

Zura のテスト
  #hage の引数が "私はズラ" の場合
    "私はズラ" が返ってくる
  #hage の引数が指定されていない場合
    引数エラーとなる

Finished in 0.00176 seconds (files took 0.08805 seconds to load)
2 examples, 0 failures

(追記)Travis CI でズラ

20160103231625

うんちく

Rspec とは

こちらより抜粋。

RSpecとは、Rubyで書かれたプログラムの動作を確認するための、テストコードを記述・実行するためのフレームワークのことです。実際に動くプログラムの動作のことを振る舞い(ビヘイビア)と呼ぶことから、それまでのテスト駆動開発のやり方をビヘイビア駆動開発(BDD)と呼ぶようになっています。

Ruby のテスティングフレームワーク。

用語 / 命令のおさらい

前述のサンプルスクリプトからピックアップ。詳細については参考にさせて頂いた記事より抜粋させて頂いた。重ねて有難うございます。

用語 / 命令 詳細 備考
describe テストのグループ化を宣言する
context テストを条件別にグルーピングする
it テストを example という単位にまとめる
before example 実行前に呼ばれる before doend
expect expect(foo).to eq bar で記述して「foobar であることを期待する」という意味になる 「エクスペクテーション」と呼ばれる
マッチャ マッチャ(matcher)は「期待値と実際の値を比較して、一致した(もしくは一致しなかった)という結果を返すオブジェクト」 こちらより抜粋、マッチャの一覧はこちらが参考になる
モック テストのグループ化を宣言する
describe 「あるオブジェクトのメソッドが[引数]で呼ばれることを期待して、呼び出された時には[戻り値]を返すようにしたい場合」に利用する こちらより抜粋
スタブ 「あるオブジェクトのメソッドが呼び出された時に、特定の戻り値を返すようにしたい場合」に利用する こちらより抜粋

その他にも用語や命令がありそうだけど…取り急ぎ。

前述のサンプルを再掲。

# Zura Class のテストを宣言
describe Zura, "のテスト" do

  #  example 実行前に Zura Class から @watashi インスタンスを生成する
  before do
    @watashi = Zura.new()
  end

  # 条件「#hage の引数が "私はズラ" の場合」
  context '#hage の引数が "私はズラ" の場合' do

    # example 実行前に @arg に "私はズラ" を代入
    before do
      @arg = "私はズラ"
    end

    # hage メソッドの返り値として "私はズラ" を期待する example を実行
    it '私はズラ' do
      expect(@watashi.hage(@arg)).to eq '私はズラ'
    end
  end

(snip)

end

も少し踏み込んで

教材

Contribute to rspec-tutorial development by creating an account on GitHub.

github.com

こちらのスクリプトから一部を抜粋したもの。

スクリプト

LXD でコンテナ一覧を取得する部分。

require "net_http_unix"

class Client
  def initialize(uri)
    @uri = uri
  end

  def client
    NetX::HTTPUnix.new(@uri)
  end

  def list_containers
    req = Net::HTTP::Get.new("/1.0/containers")
    resp = client.request(req)
    return resp.body
  end
end

hoge.rb という名前で保存しておく。

テストスクリプト

以下のようなテストスクリプトを作成。

require 'hoge'

describe Client do

  describe '#initialize' do
    context '引数 uri が設定されている場合' do
      before do
        @uri = 'unix:///var/lib/lxd/unix.socket'
      end
      it '引数は String である' do
        expect(@uri).to be_a_kind_of(String)
      end
    end
    context '引数 uri が設定されていない場合' do
      it '引数エラーとなる' do
        expect{Client.new}.to raise_error(ArgumentError)
      end
    end
  end

  describe '#client' do
    context '引数 uri が設定されている場合' do
      before do
        @uri = 'unix:///var/lib/lxd/unix.socket'
      end
      it 'object が生成される' do
        c = Client.new(@uri)
        expect(c.client).to be_a_kind_of(Object)
      end
    end
  end

  describe '#list_containers' do
    context '引数 uri が正しく設定されている場合' do
      before do
        uri = 'unix:///var/lib/lxd/unix.socket'
        @res = '{"type":"sync","status":"Success","status_code":200,"metadata":["/1.0/containers/test01"],"operation":""}'
        @c = Client.new(uri)
        allow(@c).to receive(:list_containers).and_return(@res)
      end
      it 'コンテナ一覧が String で取得出来る' do
        expect(@c.list_containers).to be_a_kind_of(String)
      end
      it 'コンテナ一覧が取得出来る' do
        response = @c.list_containers
        expect(response).to eq @res
      end
    end
  end
end

hoge_spec.rb というファイル名で保存しておく。

ローカルホストでテストを実行する

$ rake spec
/usr/local/bin/ruby -I/usr/local/lib/ruby/gems/2.1.0/gems/rspec-support-3.4.1/lib:/usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.4.1/lib /usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.4.1/exe/rspec --pattern spec/**/*_spec.rb --color -fd

Client
  #initialize
    引数 uri が設定されている場合
      引数は String である
    引数 uri が設定されていない場合
      引数エラーとなる
  #client
    引数 uri が設定されている場合
      object が生成される
  #list_containers
    引数 uri が正しく設定されている場合
      コンテナ一覧が String で取得出来る
      コンテナ一覧が取得出来る

Finished in 0.00738 seconds (files took 0.12878 seconds to load)
5 examples, 0 failures

Travis CI でテストを実行する

テストを Travis CI で実行させたいので、事前に Travis CI と GitHub のリポジトリを連携させておく。

上図のように連携を ON にしておくだけ。

20160103225021

リポジトリに push すると以下のようにテストが実行されて結果が出力される。

20160103224831

おお。

ということで

引き続き

  • マッチャ、モック、スタブ等についてメモっていきたい
  • テストを書くというのはセンスが必要な気がしているが、まずは基礎をちゃんとやりたい
  • どんな風に Rspec を勉強していいのかすら解らないのが辛い
  • Travis CI 楽しい

シェルスクリプトも

こんな風にテストフレームワークが存在するんだろうか。その前にまともにシェルスクリプトも書けるようになりたい。

元記事はこちら

シェルスクリプトも頑張りたいオプスが正月三ヶ日で解った気になる Rspec と Travis CI