2011年05月

2011年05月29日

北海道開発オフ (DevDo)

私は普段は作りたいものを作ってるのだが、今日については溜まっているタスクが多かったので、その作業をはかどらせるために参加した。

成果発表の資料はこちら(PDF 316KB)。ネタプレゼンなのでslideshareには上げてません。
2011.5.29 北海道開発オフ 今日の作業の発表

maraigue at 17:20コメント(0)トラックバック(0)イベント 

2011年05月21日

TDD Boot Camp 札幌 1.5 : ATND

前日夜に@ayako119さんに誘われたので参加。

これはTDD(Test-Driven Development、テスト駆動開発)を実習形式で学ぶ会で、札幌での開催はこれが2回目である。私は初参加。

テスト駆動開発というのは簡単に言うと、「実際に必要なプログラムに求められる仕様をコード(単体テスト)として先に書き、それに対応した実装のみを行う、というのを細かく繰り返す」ことで、仕様に沿ったプログラムを書き上げていく、ソフトウェア開発の一手法である。
より詳しい解説は
連載:[動画で解説]和田卓人の“テスト駆動開発”講座|gihyo.jp … 技術評論社
をご覧下さい。


以下では、テスト駆動開発の基本をご存知の方向けに話を進める。

今まで、私は勉強会などでテスト駆動開発について話を聞いており、独学で実践していた。
ただ、単純なロジックに対するテストコードは難なく書けるようになってきたのだが、困っていたこととして、「コードとして書くことが難しい」仕様についてどう対処するか、という実践的な知識が足りてないと感じていたのである。 (ここで「プログラムのコードとして書くことが難しい」仕様として代表的なケースは、その仕様が「CPU利用率」や「現在時刻」など、「動作しているコンピュータの状態によって決まり、かつ自力での制御が難しい」ものに依存している場合である。)

今回のTDD Bootcampで出たこれに対する答えは、「そのテストしにくい部分だけ切り分ける」というものであった。

例として、Ruby向けの単体テストツール「RSpec」を用いて、「MyTimerクラスのインスタンスは、自身が生成された時刻を保持している」という仕様をテストとして書くことを考える。単純に進めれば

# テストコード
require "./mytimer"

describe MyTimer do
  it "should contain its own created time" do
    mt = MyTimer.new
    # 以下が仕様(JUnitの「assertEquals」に相当する部分)。
    mt.created_time.should == Time.now
  end
end

とテストコードを書き、その後実際のコードを書き進めて最終的に

# 実際のコード
class MyTimer
  def initialize # Rubyのコンストラクタは「initialize」というメソッドに記述する
    @created_time = Time.now
  end
  attr_reader :created_time
end

となるであろう。

ただこれは問題があって、インスタンス生成時と実際のテスト時に時刻の秒の値が変わったときだけテストが「失敗」と判断され、それ以外の場合は「成功」と判断されてしまう

これに対する対処法として、今回は「テストしにくい部分を切り分け、テストの際のみ別のものに差し替える」という方法が述べられていた。まず実際のコードにて、時刻を取得する部分を切り分ける。

# 実際のコード
class MyTimer
  def initialize
    @created_time = time_now
  end
  attr_reader :created_time

  # 時刻を取得する部分だけ切り分け
  def time_now
    Time.now
  end
end

そしてテストコードを実行する際には、この切り分けた部分だけ差し替えて実行する。例えばRubyなら

# テストコード
require "./mytimer"

describe MyTimer do
  it "should contain its own created time" do
    $current_time = Time.now # 本当はグローバル変数なんて使いたくないんだけど
    # ここで、MyTimerを複製してテスト専用のクラスを作る
    testclass_mytimer = MyTimer.dup
    # そしてテスト専用のクラスのみ、さっき切り分けた部分を差し替える
    testclass_mytimer.class_eval{ def time_now; $current_time; end }

    mt = testclass_mytimer.new
    mt.created_time.should == $current_time

    $current_time = nil # 後始末
  end
end

とする。


あと他に興味深かったのは、テストコードを「SetUp」「Exercise」「Verify」「TearDown」の4つに区分する、というものであった。これはテストコードを読みやすくする(=後で読んだり、他の人が読んでも分かりやすくする)ためのものであって、さっきの例でいえば

# テストコード
require "./mytimer"

describe MyTimer do
  it "should contain its own created time" do
    # ----- setup(下準備をする)
    $current_time = Time.now
    testclass_mytimer = MyTimer.dup
    testclass_mytimer.class_eval{ def time_now; $current_time; end }

    # ----- exercise(実際に挙動を確かめたい処理を実行する)
    mt = testclass_mytimer.new # 今回は「インスタンスを生成したとき」の挙動を確かめたいので
    
    # ----- verify(結果が期待されていたものであるか確かめる)
    mt.created_time.should == $current_time

    # ----- teardown(必要ならば、テストに使ったオブジェクト等の後始末をする)
    $current_time = nil
  end
end

のように切り分けることになる。


今後の自分のためになった一日になったとともに、今後も単体テストを積極的に活用しようという気持ちになれた。企画の@shuji_w6eさん、その他参加者の皆様、ありがとうございました。


【宣伝】

きたる6/11(土)のオープンソースカンファレンス2011 Hokkaidoにて、「札幌C++勉強会」として出展予定です。私はC++における単体テストツール「CppUnit」について記事を書いています。



maraigue at 01:43コメント(0)トラックバック(0)プログラミング 

2011年05月19日

ちょっと理由があって必要になったので。

オブジェクト指向言語においては、一度クラスを定義すると、そのクラスのインスタンスは複数生成できることが多い。
しかし場合によっては、クラスが持つインスタンスを一つに限定したい(「Singletonパターン」と呼ばれる)場合もある。

Pythonでもこれは可能と言えば可能なのだが、多くのサンプルコードは「クラスをSingletonになるように定義する」方法であるため、Singletonなクラスをいくつも利用する場合は不便だし、Singletonにするための定義を何度も書くのはDRYでない。
例えば

class MyClass(Singleton):
のように定義するだけでSingletonなクラスが定義できれば便利である。

でぐぐってみると、こんな記事が見つかった。

Pythonでシングルトンを実装するには? - Mixnuts@BetaNews

ただこの方法だと、例えば

class MyClass(Singleton):
    def __init__(self):
        # 初期化処理
        print "初期化してます"

のようにした場合に、

a = MyClass() # こっちで "初期化してます" が表示されるのはよいのだが
b = MyClass() # こっちでも "初期化してます" が表示されてしまう!インスタンスは1つなのに!

という問題があった。

一思案してみたのだが、Singletonクラスをどう定義しようとも、MyClassの初期化処理を「__init__」というメソッドにさせている限りは解決しないことが判明し、

class MyClass(Singleton):
    def singleton_init(self):
        # 初期化処理
        print "初期化してます"

というAPIにすることで解決した。コードはこちら↓



maraigue at 03:38コメント(1)トラックバック(0)プログラミング 

2011年05月12日

3年半ぶりに、泊まりがけの自転車遠征に行ってきました。総走行距離およそ290km。

2011.5.3〜5.5 自転車旅行 札幌〜倶知安〜洞爺湖〜札幌 - <新 領 域>

北湯沢温泉近くを流れる長流川

maraigue at 13:57コメント(0)トラックバック(0)旅行 
livedoor プロフィール

H. Hiro

  • ライブドアブログ