そろそろ試験書こう

色々自動で作成されてたりしますが。

とは言え

書き始め、は今が最適なのか。あと、Web API なナニの stab ってどうやって作るのかな。なんか WebMock などという便利系な Gem がある模様。あ、でも直接云々してる訳ではないから普通に stub で云々、で良いのかな。
ええと、RSpec でテストを作るのに役立つ「モック/スタブ」のシンプルな説明 によると

[Object].stub(:[Method]).and_return([戻り値])

て事なのか。とりあえず index なアクションのナニをでっち上げてみたので試験を書いてみましょう。微妙にハマッて scaffold を参考にしてみたところログインなソレは以下の記述で OK だったようです。

  before do
    login_user
  end

その後

当分ハマッて夕方に至っております。とりあえず以下な index コントローラの試験を通す、を目標にもごもごしておりました。

class VmOperationController < ApplicationController
  before_filter :authenticate_user!

  def index
    ret = VirtualMachine.find_by_conn(current_user.conns)
    @virtual_machines = []
    ret.each { |i|
      @virtual_machines.push(i[:virtual_machines]["listvirtualmachinesresponse"])
    }

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @virtual_machines }
    end
  end

ユーザが持ってる諸々の Cloud サービスなコネクション情報を元にそれぞれでアクティブになってるホスト情報の一覧を、という形なのかどうか。view の記述は略しますが単純にホスト名をテーブルで表示してるのみ、です。
また、model の記述が以下。修正必要ですが晒す。

  def self.find_by_conn(conns)
    ret = []
    conns.each { |conn|
      virtual_machines = Hash::new
      uri = URI.parse(conn.end_point)
      compute = Fog::Compute.new(:provider => conn.provider,
                                 :cloudstack_api_key => conn.access_key,
                                 :cloudstack_secret_access_key => conn.secret_access_key,
                                 :cloudstack_host => uri.host,
                                 :cloudstack_port => uri.port,
                                 :cloudstack_path => uri.path,
                                 :cloudstack_scheme => uri.scheme,
                                 )
      virtual_machines[:conn] = conn
      virtual_machines[:virtual_machines] = compute.list_virtual_machines
      ret.push(virtual_machines)
    }
    ret
  end

で、パスしているであろう試験が以下。

    context "When user is signed in" do
      before do
        login_user
      end

      it "returns http success" do
        Fog.mock!
        Fog::Mock.delay = 0
        @mock_creds = { :provider => "CloudStack",
          :cloudstack_api_key => "xxx",
          :cloudstack_secret_access_key => "yyy",
          :cloudstack_host => "hostname",
          :cloudstack_port => "port",
          :cloudstack_path => "uri",
          :cloudstack_scheme => "scheme",
        }
        cloudstack = Fog::Compute.new(@mock_creds)
        Fog::Compute.stub(:new).and_return(cloudstack)
        cloudstack.stub(:list_virtual_machines).and_return({})
        
        get 'index'
        response.should be_success
      end
    end

mock で云々できるのは非常に有難いです。当初は stub のみで云々とかしてたのですが、なかなか上手くいかずでした。一旦 mock なオブジェクトを作って、てのがポイントだった模様。
これを踏まえて晩メシ後ももごもごな方向です。

factories.rb

以下に修正してました。ので以下な記述で不具合が出ている模様。

FactoryGirl.define do
  factory :user do
    id 1
    email    "michael@example.com"
    password "foobarbaz"
    password_confirmation "foobarbaz"
    
    after(:create) do |user|
      create(:conn, user: user)
    end
  end

  factory :conn do
    id 1
    access_key "xxx"
    end_point "http://demmy.example.com/"
    secret_access_key "yyy"
    provider "CloudStack"
    user_id 1
  end
end

scaffold で自動生成された試験が例えば以下なカンジになってて

    context "When user is signed in" do
      before do
        login_user
      end

      it "destroys the requested item" do
        conn = FactoryGirls.create(:conn)
        expect {
          delete :destroy, {:id => conn.to_param}
        }.to change(Conn, :count).by(-1)
      end

conn に代入してる部分を無理くり以下にすると試験パスしてます。

        conn = subject.current_user.conns[0]

現状、いくつかの試験が pending な状態。なんとか綺麗な状態にして、なんですがどうなるか。

追記

ええと、以下の記述はヘルパメソドに入れちゃって良いのかな。

        Fog.mock!
        Fog::Mock.delay = 0
        @mock_creds = { :provider => "CloudStack",
          :cloudstack_api_key => "xxx",
          :cloudstack_secret_access_key => "yyy",
          :cloudstack_host => "hostname",
          :cloudstack_port => "port",
          :cloudstack_path => "uri",
          :cloudstack_scheme => "scheme",
        }
        cloudstack = Fog::Compute.new(@mock_creds)
        Fog::Compute.stub(:new).and_return(cloudstack)
        cloudstack.stub(:list_virtual_machines).and_return({})

やってみましょう。ヘルパではなくて spec/support/controller_macros.rb に以下を追加。

  def fog_mock_init
    Fog.mock!
    Fog::Mock.delay = 0
    @mock_creds = { :provider => "CloudStack",
      :cloudstack_api_key => "xxx",
      :cloudstack_secret_access_key => "yyy",
      :cloudstack_host => "hostname",
      :cloudstack_port => "port",
      :cloudstack_path => "uri",
      :cloudstack_scheme => "scheme",
    }
    cloudstack = Fog::Compute.new(@mock_creds)
    Fog::Compute.stub(:new).and_return(cloudstack)
    cloudstack.stub(:list_virtual_machines).and_return({})
  end

で、試験が以下なカンジに。

    context "When user is signed in" do
      before do
        login_user
      end

      it "returns http success" do
        fog_mock_init

        get 'index'
        expect(response).to be_success
      end
    end

list_virtual_machines が空の Hash を戻してるのが良いのだろうか。とりあえずこれでスタート地点に立てた模様です。頑張ろう。

TODO 控え

ええと

  • 以下な routing 追加して云々
  resources :vm_operation do
    member do
      get :start, :stop
    end
  end
  • create なアクション
  • start/stop なアクション

ここは早めに実装マストなのでこれから検討。

新規に追加

どうも Rails 的には以下な作法らしい。

                         POST   /conns(.:format)                  conns#create
                new_conn GET    /conns/new(.:format)              conns#new

new なアクションでフォームが出てきてそこから POST するのは create アクションなのか。
以下な設定を追加しつつ spec/routing な試験を確認してみる方向。

  resources :vm_operations, only: [:new, :create, :index] do
    member do
      get :start, stop
    end
  end

member てのがアレですが (何
で、盛り込んで rake routs したら以下が出力されました。すばら。

      start_vm_operation GET    /vm_operations/:id/start(.:format) vm_operations#start
       stop_vm_operation GET    /vm_operations/:id/stop(.:format)  vm_operations#stop
           vm_operations GET    /vm_operations(.:format)           vm_operations#index
                         POST   /vm_operations(.:format)           vm_operations#create
        new_vm_operation GET    /vm_operations/new(.:format)       vm_operations#new

一応これを踏まえて routing の試験も書いておくことに。
つうか、コントローラな class の名前を単数にしてたのも含め、ここでもハマるなど。週末はだらだらしてたのですが、なるべく朝練でこのあたりの実装をアレして某所に連絡を、と思いつつ (ry