Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

RSpecの作者が振り返る歴史(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

RSpecの作者が振り返る歴史(翻訳)

私がTDD(テスト駆動開発)をチームで教え始めたのは2001年のことでした。当時のTDDはまだかなり新しい概念でしたので、テストを自動化したチームもほとんどなく、XP(エクストリームプログラミング)やTDDについて聞いたことがある人も皆無でした。テストを最初に書くことで設計を進めるという概念は当時まったく知られていなかったので、TDDを理解するのに皆とても苦労していました(20年経った今でも、この事実が完全に変わったとは言えません)。

思い返せば、あの当時は厳しい状況でした。最善を尽くしてTDDの概念を説明し、どうにかしてチームの関心を惹こうと心血を注いだものです。TDDの普及は困難の連続で、私もかなり手こずりました。もっと言えば、私に教わる人たちがTDDの理解と実践に苦労していたのを解決したかったのです。

当時よく遭遇した問題のひとつは、testという言葉そのものにつまづく人が多かったことです。testという言葉も含め、TDDに関連する語彙は当然ながらどれもこれも「テスト中心」だったので、当時は誰もが非常に戸惑いました。「存在しないコードはテストしようがない」と言われるのもざらでしたし、「あなたの前提がそもそもおかしいんだよ」とドヤ顔で返されることすらありました。私が大きな価値を感じた概念や手法が、testという言葉を使った途端に「たかがテストじゃないか」とばかりに軽くあしらわれるありさまでした。私はそこを変えようとしたのです。

突破口を探しているうちに、サピア=ウォーフの言語的相対性仮説というものを知りました。これは「同じアイデアでも言葉を変えて伝えると、アイデアの受け止められ方や解釈が変わる」ものです。同じことに気づいた人は他にもいて、Aslak Hellesøy、Dan North、Liz Keoghらが早くからこの分野に取り組んでいたおかげで、多くの洞察を得られました。

そこで私は、トレーニングで使うツールを作り始めました。私はテスト中心の語彙をなくしたかったのです。とある預言者の言葉1を借りれば「testという名前がよくない」ということです。2003年までに私は、RubyでTDDを教え始めました。当時のRubyはまだニッチな言語で、Ruby on Railsもまだ世界を席巻する前でしたが、これらのアイデアを実装するのにふさわしいオブジェクト指向言語であることを私は評価していました。

互いに考えの似通った人たちとペアを組んでいるうちに、彼らのテストがおしなべてtest_should_do_this_thingのような名前になっていることに目を止めました。testという言葉がまだ残っていたものの、意図を伝えるのによりふさわしい言葉shouldが使われていたのです。

このtestという語を巧妙に消し去るにはどうすればよいでしょうか?Rubyなら簡単に取り除けます。そこで私は、完成したソフトウェアが持つ「べき(should)」「振る舞い(behavior)」を「記述(describe)」する方法として、TDDを教えるためのツールを作ることを思いつきました。当時このアイデアはまだ新しかったのですが、TDDの生みの親たちが一貫して主張していた「自動テストは、完成したソフトウェアが持つ"べき" "振る舞い"を伝えるものである」がそこに盛り込まれていました。

私が最初に作ったツールはdescriber.rbという以下の小さなRubyファイルで、testという語を消し去ることだけを目的としています。

require 'test/unit'

Description = Test::Unit
Context = TestCase

# Test::Unit::TestCase.suiteを再定義して"test"ではなく"should"で始まるメソッド名を使うようにする

このツールを使うと、「テスト」は以下のように書けます。

require 'describer'

class SimpleAdditionDescription < Description::Context
  def should_add_one
    assert_equal 2, add_one 1
  end
end

これでtestという言葉を使わずにテストが書けるようになりました。素晴らしくシンプルな解決策です。TDDを新たに実践する人が、Ruby組み込みの標準ライブラリに存在する堅牢なランナーセットなどの素晴らしいツールをすべて使い、しかもtestという言葉を使わずにテストを書けるようになりました。testという言葉が繰り返し出現して認知的不協和を生じることのないように仕組んだのです。

このツールをTDDのトレーニングに取り入れました。私のワークショップでは、このファイルを使ってソフトウェアの"振る舞い"を記述させ、トレーニング中はtestという言葉を使わないよう努めました。すると「テスト」という言葉にまつわるもろもろの悩みが一気に解消されたのです。

このトレーニングを最終日まで毎回繰り返し、最終日にはこんな種明かしをしました。「本トレーニングでソフトウェアを"記述"するために書いているものが、実はそのまま、ソフトウェアをテストして"振る舞い"の正しさを確認するために実際に実行可能な自動テストスイートを表しているのです」。そしてワークショップの参加者に「ここを卒業したら実際のRubyのテストは以下のように書きましょう」と教えました。

require 'test/unit'

class SimpleAdditionTest < Test::Unit::TestCase
  def test_add_one
    assert_equal 2, add_one 1
  end
end

この方法は見事に功を奏しました。参加者が「標準的なツール」を使うようになることと、参加者がこれまでと違う新たな考え方でテストにアプローチできるようになることを両立したいと考えていました。テスト用語の導入をあえてワークショップの最終日に回したことで、参加者は私の意図したとおりの視点から問題にアプローチしながら、標準的なツールを使うようになりました。私のトレーニング方法によって、参加者が自分たちに必要な価値を学ぶようになっただけではなく、より多くの人々、つまりTDDに強く抵抗してきた層にもリーチできるようになりました。

当初は気づきませんでしたが、ワークショップの参加者がアサーションのパラメータを取り違えてしまう問題が残っていました。アサーションのexpected(期待値)とactual(実際の値)をつい逆さまに書いてしまいがちで、エラーメッセージが混乱する原因になっていたのです。正直に申し上げれば、私もたまにこのようなミスをすることもありましたが、経験を積んでいたおかげですぐ間違いに気づけました。パラメータ逆さま問題を解決するために、expectationをより平易な言葉で書くことにしました。ブロックを受け取るassert_thatというメソッドを追加して、返されたオブジェクトに対してアサーションを使えるようにしました。

assert_that { expected }.equals actual

このライブラリファイルは、もともとワークショップの問題を解決するためだけに作ったのですが、そのままでは多くのコードが必要だったので、もっとシンプルな方法を探しました。これはRubyの力を借りれば解決できます。そこでメタプログラミングの世界に足を踏み入れ、以下のようにアサーションをObjectクラスに直接足しました(リリースされるライブラリとしては悪手ですが、教育用としては実に有用でした)。

class Object
  def should_equal expected
    assert_equal expected, self
  end

  # その他のアサーション
end

この変更で教育用ツールは完成しましたが、当時一般にはリリースしておらず。私のワークショップにしか存在していませんでした。このツールを自分のワークショップ以外で使ってもらうつもりはありませんでした(標準ライブラリのツールを使うべきだと思っていたので)。xUnitパターンの概念は十分普及していましたし、ほぼすべての言語で利用可能で、サポートも充実していました。今さら新しいライブラリを増やしてメンテナンスに時間を取られるのは大きな過ちだと思っていたのです。

この新しいツールは言葉の使い方を変えるだけで、誰にも必要とされていないと思っていました。今にして思えば、私の活動が有意義なのはワークショップという場であり、現場では必ずしもそうではないと考えていた私が浅はかでした。

私はこのツールをリリースするつもりは毛頭ありませんでした。xUnitは既に存在していましたし、xUnitで目的を果たせました。後にRSpecと呼ばれるようになったこのツールは、私にとって単なる教育用ツールでしかなく、ワークショップの外で使うべきものではないと思っていました。しかしリリースしないわけにいかない事情が発生したのです。

RSpecの誕生

2000年代半ば、Bob MartinはTDDを紹介するときに、私のやろうとしていたのと同じことを印象付けようとしていました。Bobもまた、他の人の言葉を別の言葉で言い換えていました。いわく「テストとは検証(verification)ではなく仕様(specification)である」と。さらに「ソフトウェアのテストとは、実行可能な仕様のことである」と。

この切り口は私も気に入っていたのですが、「仕様」という言葉がどうも引っかかりました(今でもそうです)。インターネットの黎明期からRFCや各種仕様書を元にさまざまなソフトウェアを実装してきた私にとって、「仕様」(specification)という言葉は予約語であり、他の意味で使われるべきではないと思えたので、新しい言葉を探す必要を感じていました。

しかしBob Martinは当時の私にとってヒーローであり、「仕様中心」の語彙で支持層を広げていました。そのBobが、私がワークショップ向けに作ったツールのデモを見て、ぜひこのツールをリリースしようと持ちかけてきたのです。Bobは、ツールの語彙を「仕様中心」に整備することに専念するよう熱心に働きかけました。

私は、RSpecという名前は良くないと今でも思っています。最初にリリースを考えた頃はまだ「describer」と呼んでいたので、当時の流行りっぽくeを捨ててDescribRにしようかとも思いましたが、この名前にも満足できず、どんな名前にするかをBob Martinと議論する資格が自分にあるとも思えませんでした。このような経緯でRSpecが誕生したのです。

RSpecは2005年にリリースされました。

当時の私は、教育用ツールとしてのRSpecには大いに満足していましたが、実際に使われるべきではないと思っていました。しかし多くの人々がこのアイデアに惚れ込み、この方法でテストを書きたいと思うようになったのです。RSpecがこれほどまでに広く、そして急速に普及したことに驚いています。ある調査によると、新規Ruby on Railsプロジェクトの90%が、Rails組み込みのxUnitスタイルのフレームワークではなくRSpecを使っているそうです。それから間もなく、他の言語でも「RSpecクローン」が多数出現するようになりました。

DSLとしてのRSpec

RSpecがリリースされた当時、DSL(ドメイン固有言語: Domain-Specific Languages)という考え方が流行り出していました。私は言語を使って人々のソフトウェアに対する考え方を変えるのが好きなので、ソフトウェアの動作を指定する完全なDSLを作成すれば、RSpecがさらに良くなるのではないかと期待したのです。

私はJoe O'Brienと組んで、RubyConf 2005でRSpecの最初のバージョンを発表しました。使っていた語彙は現在と異なりますが、この時点でアイデアは既に固まっていました。RubyConf 2005でJoeと私が発表したのは、最終的に次のようなものでした。

specify "add numbers" {
  it_should "add one to the provided numer" {
    add_one(1).should_equal 2
  }
}

RSpecをこのように変更することで、人々がコードのことばかり考えるのではなく、ソフトウェアの「望ましい振る舞い」に注目することを期待しました。RSpecをDSL化したことについては、今でも満足しています。

プロジェクトとの別れ、そして教訓

このプロジェクトのコードに対する私の実質的な最後の貢献は、RSpecを完全なDSLにしたことでしょう。私自身は依然としてRSpecを使っていませんでしたし、RSpecはあくまで教育用ツールだと考えていたので、「標準」のテストツールを使いたかったのです。また、当時の私は仕事でさまざまな言語を使っていたので(今もそうですが)、どの仕事でも同じようなツールを使いたいと思っていました。せっかくTDDの概念を正しく学んだ人々が、DSLとオブジェクトモデルとの間で厄介な相互作用を引き起こす非標準ライブラリをわざわざ選ぶ理由が私には理解できませんでした。

RSpecが使われる理由が当時の私には本当に理解できませんでしたし、自分自身も使わなかったので(私はRSpecを使い始めていたプロジェクトでしか使ったことがなく、自分からRSpecを選んだことはありません)、自分がこのプロジェクトの管理者としてふさわしいと思えませんでした。このプロジェクトの将来について私よりも強く期待している人がたくさんいましたので、2006年か2007年のある時点で、これまでで最も多作かつ重要なコントリビュータであるDavid Chelimskyにこのプロジェクトを譲りました。

Chelimskyは、このプロジェクトの世話役にうってつけでした。これについて私の人選はこれ以上ないほど的確だったと思います。RSpecをDavid Chelimskyという有能な(特に私よりも有能な)コントリビュータに譲ったことは、おそらくプロジェクトに対する私の最もポジティブな貢献でしょう。私がRSpecを作ったことよりもずっとポジティブです。

また、(これまでの歴史が示すように)私が将来に渡ってソフトウェア業界全体に貢献できるのはRSpecだけではないという自負もありました。2007年頃の私は「自分は一発屋ではない」と主張していました。私にできることはまだまだあると思っていますが、RSpecのときと同じように時計の針が再び動き出すという自信はありません。正直に申し上げると、私はこのことで長年にわたって非常に悲しい思いをしました。

教訓、そして陳謝

私もたくさんの失敗をしてきました。比較的ささやかではあるものの、私の主たる失敗を申し上げます。私が作ったもの(RSpec)は、人々に楽しんでもらいながらテストに対する考え方を変えることができましたが、それをリリースして人々に思い思いの方法で使ってもらうことについて私は根強く抵抗してしまいました。何年も後になって、RSpecに出会うまでTDDやBDD、あるいはテストそのものをまったく理解できなかった人たちから「ありがとう」というメッセージを受け取るようになって、初めてそのことに気がつきました。いただいたメッセージに心から感謝するとともに、皆さんがRSpecを独自の方法で使うことによい顔をしなかったことを申し訳なく思っています。

技術面で言うと、アサーションをObjectクラスに足したのは失敗でした。これでRSpecの初期ユーザーを大勢苦しめてしまったので、プロジェクトの初期段階では、アサーションの形式を再検討してexpectationを変更するよりも、この決定を撤回して問題を回避することに多くの時間を費やしました。これは後のバージョンでexpect(expected).to equal(actual)と修正されました。私見ではこれがexpectationを記述する最も完全な方法であり、Objectクラスに入れるというミスを犯す前の私が行ったオリジナルの実験に近いものです。もし初期のRSpecでこの点に悩んでいた方がいらっしゃったら、私の間違った決定で二重に苦しめてしまったことを、そして頑丈なソフトウェアを作ろうとする尊い努力を妨げてしまったことをここにお詫びしたいと思います。

もうひとつ悔やまれるのは、自分の功績を奪われた事件です。私の初期のメンター(自己中心的で嫌われ者であることを自ら露呈したので名前は伏せます)が、RSpecを作ったのは自分であり、RSpecが作られた後に自分が作った部分こそがRSpecのベースでありインスピレーションの源であると吹聴しようとしたのです。RSpecは多くの人から寄せられたコードや知識の貢献の上に築き上げられていますが、この人物によるプロジェクトへの実際の貢献は最小限でしかなく、不埒にも初期の頃に他人のコードをパクってはライセンスヘッダを捨てていたに過ぎませんでした。

そして最後に、自分が作ったものから遠ざかってしまったことを悔やんでいます。自分が作ったものが違う形で使われることを気持ちよく受け入れていれば、私もそうした場でより便利なツールを作れたでしょうし、さらに多くの人の助けになれたでしょう。より多くの人の助けになれなかったことを申し訳なく思います。

感謝の気持、そして将来

微力ながらソフトウェアコミュニティに貢献できたこと、そして人々が自分たちの可能性に気づく手助けができたことに感謝します。そして今後も、より多くの人々の可能性を引き出すために役に立ちたいと願っています。私の作ったものが評価され、世界中で使われ続けていることに、私はいつまでも感謝しています。私がリリースを望まなかったものをリリースしたお礼にと、たくさんのビールをいただいたことにも感謝いたします。

私にもまだまだできることがあると期待していますが、当面は個人やチームが最高の仕事ができるためのサポートに集中することにします。私はこれからも、自分ができる多くのささやかな貢献に満足し、私の貢献をサポートしてくださる方々と一緒に働ける機会に感謝していきたいと思います。

そして、今後も私を助けてくださる人たちとともに貢献できることに感謝しつつ、ソフトウェアの開発を関係者全員にとって少しでもよいものにしていきたいと思っています。


  1. 訳注: 映画『ビッグ・リボウスキ』のセリフ(参考)。 

CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。