ちなみに

火曜日の空は僕を押しつぶした。

dRubyを使用したコードのテスト方法

研究でdRubyを使っているのですが、そのとき詰まったのがテストの方法で、しばらく悩んでしまった
今回は僕の使っている方法を書き残しておこうと思う
ちなみに、RSpecの使用を前提としている

通信をおこなわない場合

まずは、通信を行わない場合について考える。

単純に、ちゃんとdRubyサービスを開始しているかをテストするには、should_receiveを使って以下のように書く

it 'should connect server' do
  uri    = 'druby://...'
  DRb.should_receive(:start_service).with(uri, @server)
  @server.start
end

@server.startの中でDRb.start_serviceが呼ばれなければエラーになる。このときwithで@server自身を指定しているのは、内部で自分自身をフロントにしてdRubyサービスを開始するようにしているからである
フロントになるオブジェクトはサーバクラスの中で生成される場合はテストがしにくくてたまらないので、そうならないように設計すると良い

次に、クライアンと側だが、これはサーバと同じでDRbObjectにたいして、should_receive(:new_with_uri)を呼ぶだけなのでコードは割愛する


実際の内部動作をテストする場合もモックを使うと上手くいく
この場合、欲しい情報は、通信を行った後に得られる結果のみである。そしてその結果をうまく処理できているかを見たい。

describe client do

  before do
    @unformatted_data = ...
    @formatted_data   = xxx
    @server = mock('server', :get => @unformatted_data)
  end

  it 'syould return formatted server return value' do
    @client.should_receive(:format_data).with(@unformatted_data).and_return(@formatted_data)
    data = @client.get_data
    data.should == @formatted_data
  end
end

beforeの中でサーバのモックを作り、スタブを定義している。こいつはgetが呼ばれたら@unformatted_dataを返すだけだが、実際はDRbObjectであることを想定している
exampleの中では@clientがformat_dataを呼ぶことを期待している。さらに返す値も差し替えているので、もし内部でformat_dataが呼ばれたら、get_dataの返り値は@formatted_dataのはずであるので、それも確かめている。


このようにして、通信を行わずにテストをするときはモックとスタブ、そしてshould_receiveを使うと良い

通信を行う場合

通信をさせたい場合は以下のようなヘルパメソッドを書いておけば便利である

def on_service(front = [], port = 12345, host = 'druby://localhost')
  uri = "#{host}:#{port}"
  DRb.start_service uri, front
  yield
ensure
  DRb.stop_service
end

このメソッドにブロックを与えると、その中では指定したホスト、ポート、フロントのdRubyサービスが有効になっている

on_service({:name => 'tomohiro', :age => 22}) do
  @server = DRbObject.new_with_uri('druby://localhost:12345')
  @server.keys #=> [:name, :age]
  p @server[:name] #=> "tomohiro"
end

これを用いて、サーバのブラックボックステストなどを行っていく

まとめ

  • モック、スタブを使った方法をみた
  • ヘルパを使って実際にサービスとやりとりする例をみた

通信を行ったり、スレッドを使ったりする場合はテストが難しいことがあるので、まずは、テストし易い設計を考える。それからどうすればテストできるかを考えると良い