RSpecを自分で作って学ぶ(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社エンジニアの「?」を「!」に。原著者の許諾を得て翻訳・公開いたします。Dąbrowsk以下の記事を参考に、テスト対象コードとテスト用コードを1つのファイルにまとめて記述しておくと試しやすいでしょう。RSpecを自分で作って学ぶ(翻訳)RubyのDSLとメタプログラミングのやさしい入門記事です。言語を新たに作ることで特定の問題を記述する、プログラミング技法の一種です。Railsフレームワークのルーティングや、RSpecの構文はDSLのよい例です。プログラミング技法の一種で、指定のコードに別のコードを生成する能力を与えます。メタプログラミングはDSLとの関連が深く、メタプログラミングが使えないとDSLを使えません。RubyコードをテストするDSLのテストツールであり、Rubyで記述されています。Railsアプリで簡単に使えます。DSLとメタプログラミングは、どちらもそれ単体だけで大きなトピックになりますので、本記事では楽しくわかりやすい方法でざっくりご紹介するにとどめます。RSpecを使ったことのない方には、先にRSpecを少しでも使ってみてからお読みいただくことをおすすめします。独自のRSpecをこしらえる前に、DSLやメタプログラミングの使い所や使うべき理由がまだよくわからない方向けに、簡単なコード例を1つご覧いただきます。次の設定用クラスで考えてみましょう。endendこれは設定用クラスの模造品です。環境やサーバーのドメインを設定するには、次のような呼び出しをかけなければなりません。'domain.com'この設定方法を実際に使うわけではないので、このままでは何だかつまらないですね。メタプログラミングをカッコよく使って、もう少し楽しくしてみましょう。ここではinstance_evalメソッドを使うことにします。インスタンスをevalって?はい、本記事で最も多用されるのがこのメソッドです。先ほどの新しい設定用クラスを題材に、意味のあるコード例を書いてご説明します。'domain.com'endオシャレ感増し増しの設定方法になりましたね。先ほどの実装から唯一変更された点は、initializeメソッドを追加して、ブロック引数を1つ取れるようにしたことです。このメソッドで、本日の主役であるinstance_evalを呼び出すわけです。instance_evalが何をするかというと、渡されたブロック内のあらゆるコードを「Configクラスインスタンスのコンテキストで実行」します。言い換えれば、渡すブロック内ではいちいちconfig.プレフィックスを付けなくても(そのインスタンス内の)どんなオブジェクトにもアクセスできるということです。独自のRSpecをこしらえるからには、その独自RSpecでテストする対象が必要です。ここではTDD(テスト駆動開発)方式ではなく、最初にサンプルのクラスを作成して本記事での説明に役立てたいと思います。endend張りぼてのクラスができたので、RSpecで以下のようなテストを書いてみましょう。endend既にinstance_evalについて学んだので、このようなコードの書き方について薄々見当がついているかと思います。このテストで使われているすべてのメソッドをどう自作するかを考えることにしましょう。クラスまたは文字列を1つの引数として受け取ります。どちらを渡しても出力は文字列になります。documentationを追加してテストを実行してみればおわかりいただけると思います。指定された実行パスのdescriptionを受け取ります。describeメソッドのブロック内に記述することで、テストの概要をドキュメントらしい形式で表示できます。私もRSpecのソースコードを詳しく見たわけではありませんので、実際の実装と比べたところで相当かけ離れているかと思います。toとnot_toは、それぞれ==演算子と!=演算子と同等です。eqは読みやすくするためのシンタックスシュガーです。少なくともこの6つのメソッドを実装しなければなりません。describeメソッドから。endendこのメソッドは引数を2つ取ります。1つ目はdescriptionを、2つ目はブロックをそれぞれ1つずつ取ります。これで例の張りぼてクラスのインスタンスを作成してみましょう。何かするendここでの問題は、Describe.newではなくdescribeと書きたいことです。修正のためにヘルパーメソッドを1つ作らなければなりません。何かチェックするendだいぶいい感じになってきました。先の例では、2つのdescribeブロックがネストしているので、ネストをサポートしなければなりません。endendこれでdescribeブロック内に別のdescribeブロックをネストできるようになりました。rspec.rbを実行できます。今度は、実行パスを記述するitメソッドを実装します。endend次は、指定の結果のexpectationを得るコードを追加します。そのためには、expectメソッド、toメソッド、eqメソッドの実装が必要です。これらについては、新しくExampleクラスを作ってそこに追加します。endendこのコード例を実行しても何も起きません。チェイン内でメソッドを呼び出せるようになっただけで、まだ何も実装されていないからです。expectメソッドは何をすべきなのでしょうか?あるメソッド呼び出しの結果を代入して、toメソッドで取り出せるようになればよいのです。endendこのeqメソッドは単なる==の置き換えですが、ここでの課題は、これをtoメソッドに渡して結果と比較することです。その答えはProcです。endend締めくくりは、toメソッドの実装です。toは、結果をexpectationと比較します。eq(12)を実行してみると、trueと出力されました。やりましたね!テスト結果を出力するロジックは実装できましたが、まだコンソールにテスト結果が何も表示されません。Exampleクラスにpublicなreaderを追加してテスト結果をそこに保存し、Describeクラスからアクセスできるようにしましょう。:resultenddescribeブロックやitブロックがたくさん使われることはわかっているので、それらをDescribeクラスに集めてテスト出力を生成するときにアクセスできるようにします。:describesend締めくくりは、出力用メソッドの実装です。メソッド名をtestにして、Describeクラスに追加します。rspec.rbを実行すると、以下が出力されます。12ところでspecがパスしたか失敗したかはどうすればわかるのでしょうか?失敗の場合は赤で、成功の場合は緑を表示しましょう。gemを追加しなければなりません。gemの#colorizeメソッドを用いて、テキストの色を指定できます。このメソッドは色のシンボルを引数に取ります。ここでは:redや:greenを指定します。example_node.test_resultを呼び出せば使うべき色を検出できます。最終的なコードは以下のようになります。endendrspec.testさらに手を加えるには非常にシンプルかつ限定的な実装ですが、皆さんがRSpecのしくみを知る手がかりになればと思います。クラスの宣言を別ファイルに切り出すなり、to_notメソッドをサポートするなり、好きに改造するとよいでしょう。RSpecが既に存在しているおかげで、アプリをテストする独自RSpecを何もないところからこしらえなくて済むありがたいことですね。テスティングのスキルの重要性にお気づきでしょうか?テストを正しく書き始めることが、唯一のファーストステップです。Developmentの無料ebook』をどうぞお役立てください。コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。enno.jpを運営。実は最近Go言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。Storageを使ってはいけない(翻訳)#Tags#会社紹介#自社サービス#勉強会#Rails#PHP#デザイン#実績#週刊RailsウオッチCONTACTお気軽にご相談ください。(翻訳)CONTACTTechRachoでは、パートナーシップをご検討いただける方からのご連絡をお待ちしております。お気軽にご意見・ご相談ください。