«前の日記(2010-05-12) 最新 次の日記(2010-05-19)» 編集

KONO's Diary-休むに似たり



2010-05-15 [長年日記]

_ [ruby] FlexMockでDBI

今日は完全にプログラマーな話。

TDDを鋭意修行中なのだけど、ユニットテストではセットアップが大変な外部オブジェクトの代わりにモック/スタブを使う、という話を聞いた。で、モック/スタブのためのRubyのライブラリでFlexMockというのがメジャーらしい。

ぼくの場合、「セットアップが大変な外部オブジェクト」といえばまずはリレーショナルデータベースである。で、FlexMockでDBIのモックを作ってみようとやってみたら、思いのほか戸惑って、時間がかかってしまったので、出来上がった結果をご紹介したら多少のお役に立てるかもと思った次第。

たとえば、こんなコード。

# testodbc.rb
require 'rubygems'
require 'dbi'
 
def dbdump
  begin
    dbh=DBI.connect('dbi:odbc:testdsn','userid','password')
    sth=dbh.execute("select * from test1")
    while row=sth.fetch_hash do
      print row["id"] + ' ' + row["name"]+"\n"
    end
  ensure
    sth.finish
  end
end

"testdsn"というDSN名でODBCドライバが設定されているデータベースに'userid','password'で接続し、test1というテーブルのフィールド"id"と"name"をコマンドラインにダラダラと出力する、という関数である。 これをFlexMockを使ってテストするコードは、こんな感じ。

# TC_testodbc.rb
require 'rubygems'
require 'test/unit'
require 'tempfile'
require 'flexmock'
require 'testodbc'
 
class TC_aitherodbc < Test::Unit::TestCase
  include FlexMock::TestCase
 
  def test_db_dump
    row1,row2,row3=Hash.new,Hash.new,Hash.new
    row1["id"]='1'
    row1["name"]="foo"
    row2["id"]='2'
    row2["name"]="bar"
    row3["id"]='3'
    row3["name"]="baz"
    expectation="1 foo\n2 bar\n3 baz\n"
     
    flexmock(DBI).should_receive(:connect).with('dbi:odbc:testdsn','userid','password').and_return{
      flexmock('dbi') do |dbiobj|
        dbiobj.should_receive(:execute).with("select * from test1").and_return {
          flexmock('rcdset') do |rcd|
            rcd.should_receive(:fetch_hash).with_no_args.and_return(row1,row2,row3,nil)
            rcd.should_receive(:finish).with_no_args.once
          end
        }
      end
    }
    temp=Tempfile::new("teststdout")
    $stdout=temp
    dbdump
    $stdout=STDOUT
    temp.close
    temp.open
    result= temp.read
    temp.close
    assert_equal(expectation,result)
    end
end

DBI.connetを呼ぶと返されるデータベースハンドル、executeを実行すると返される結果セット、fetch_hashを呼ぶと返される行(…をハッシュオブジェクトにしたもの)、すべてFlexMockを使ってニセモノを作り、それでテストしている。

fetch_hashを呼ぶところ、最後が

and_return(row1,row2,row3,nil)

となっていて、これは一回目にrow1, 二回目にrow2, 三回目がrow3で4回目はnilが返る、という意味だけど、ずっとここを

ar=[row1,row2,row3,nil]
...and_return(ar)

という配列を返そうとしてうまく行かなくて悩んでしまったのだった。

こちらの例ではFileオブジェクトのreadlineのモックで配列を返してうまく行っているので、同じようにやればうまく行くだろうと思い込んでしまった。いつも見よう見まねなので、こういう時はなかなかうまくいかない。

ともあれ、あとupdateする場合や、select/updateでprepareステートメントを使う場合等もFlexMockを使ったテストを書けるようになると、非常に幸せになれそうな気がするのだった。

[]



あわせて読みたい rss
«前の日記(2010-05-12) 最新 次の日記(2010-05-19)» 編集
カバー画像はWikimedia Commonsより。
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
画像提供元:http://commons.wikimedia.org/wiki/Image:Field_of_hay_bales_-_omeo.jpg