give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

チェリー本増刷記念!?技術書の改訂版ができあがるまで 〜プロを目指す人のためのRuby入門 改訂2版の制作裏話〜

はじめに:改訂2版の増刷が決まりました!

先日開催された「VeriServe Test Automation Talk No.3」でもお伝えしましたが、「プロを目指す人のためのRuby入門 改訂2版(通称・チェリー本)」の増刷が決まりました🎉

ご購入してくださったみなさん、どうもありがとうございます!
改訂2版もたくさんの人に読んでいただいて、たいへん嬉しいです😄

改訂版の制作は楽ちん?→ NO!!

ところで、「本の改訂版って、ちょいちょいっと古い記述を加筆修正して出版すればいいだけだからラクなもんでしょ?」と思う人がいるかもしれません。

ですが、今回の「プロを目指す人のためのRuby入門 改訂2版」に関していえば、全然ラクではありませんでした!!第1版を制作したときと同じか、それ以上にハードだったかもしれません。。

そこで、このエントリでは改訂2版の増刷を記念して(?)、制作を開始してから発売するまでの制作裏話をあれこれ書いてみたいと思います。

参考:第1版の制作裏話もあります

第1版を出版したときのエピソードは以下のエントリにまとめてあります。
制作の大まかな流れ自体は第1版と大きく変わってないので、先にこちらを読んでもらった方がこのエントリの内容が理解しやすいかもしれません。

blog.jnito.com

ちょっと長いので先に目次を載せておきます。

それでは以下が本編です!

2017年11月から2020年10月まで:改訂版に向けたネタ集め

改訂版を制作が決定するずっと前から、というか、第1版が発売された直後から、実は改訂版の執筆に向けた準備は始まっていました。

Twitter上の反響や、本書について書かれたブログ記事を読んだりすると、初心者さんがはまりやすい箇所や誤解しやすい箇所があることがわかります。

僕はプログラミングスクール「フィヨルドブートキャンプ」でメンターもやっており、生徒さんがチェリー本を読んでつまづきやすい部分があることも把握していました。

こうしたつまづきポイントを見つけたら、編集者さんと情報交換をしている掲示板に「改訂版で書き直したいトピック」としてネタを記入していました。

改訂版を出すならここを直したい、というメモです

また、僕はQiitaやZennに毎年「Ruby 3.xの新機能」といったまとめ記事を書いていますが、これは実は改訂版を執筆するときにRubyの仕様変更に伴う差分がどこに発生するか、というのを把握する目的も兼ねていました。

zenn.dev

2020年11月:改訂版制作の話が持ち上がる

編集者さんとは以前から「改訂版を制作するならRuby 2.xではなく、Ruby 3が出てからにしましょうか」という話をしていました。

2020年の後半にはRuby 3.0が2020年12月にリリースされることがほぼ確実になったので、「そろそろ改訂版の制作に着手しても良いのでは」という話になり、改訂版制作プロジェクトがスタートしました。

2021年1月:目次案の作成

改訂版の執筆にあたり、まず目次案を作成しました。具体的には第1版との差分をリストアップしていく感じです。「新たにこの項目を増やしたい」「ここはもう不要なので削りたい」というような内容を僕の方で作成しました。ただ、増える箇所が大半で、削る箇所はほとんど無かったですね。この時点でページ増になることは確実でした😅

実際に作った加筆修正リストです

また、Rubyは毎年12月25日に新バージョンがリリースされます。そのため、「Ruby 3.0に対応」を売りにするなら遅くともRuby 3.1がリリースされる前、すなわち2021年12月25日より前にする必要があることも確認しました。
当初は2021年11月末を出版日にする予定だったのですが、諸事情により最終的な発売日は2021年12月2日になりました。

FAQ: 2021年末に発売するならRuby 3.1に対応すればいいんじゃないの?

「なんでRuby 3.0なの?発売直後に3.1が出るんだから、Ruby 3.1に対応すれば良かったんじゃないの?」という質問をもらうことがよくあります。
これはごもっともな意見なのですが、Rubyの仕様はリリース直前に大きく変更される可能性もあります(追加されるはずだった新機能の導入が見送りになるなど)。
そうなると、書籍の発売直前で内容の修正が必要になって発売を延期したり、実際には導入されていない新機能の解説を含んだまま出版されたりする恐れがあります。
ネットの記事と違い、紙の本は簡単に修正することができないため、執筆時点で確実に仕様が固まっているリリース済みのバージョンを対象にしなければなりません。よって、今回はRuby 3.0を対象バージョンにしました。
本当は僕もRuby 3.1を対象にしたかったんですけどね……😭

2021年2月:原稿の加筆修正スタート

編集部内の企画会議はすんなり通過できたので、2月から原稿の加筆修正をスタートしました。
まず、第1版を改めて読み直して、「ここは加筆 or 修正したい」というポイントに書き込みと付箋を入れていきました。

ここに追記、ここは変更、みたいなメモを入れていきました
付箋を付けた箇所は100個以上ありました
で、どうやって原稿の加筆修正を行うか?

さて、「ここを直したい」という部分はざっと決まりましたが、このあとで困ったのが「どうやって原稿を修正するか」です。

プログラマの感覚だと、「GitHubに置いてある第1版の原稿を修正してプルリク作ればいいんじゃない?」と思うかもしれませんが、残念ながらDTPデータはバイナリデータなのでGitHubで管理できません。てか、そもそも著者に共有されていません。

いちおう、DTPデータの元となるMarkdownの原稿はGitHubに置いているのですが、第1版の執筆時はDTPデータ作成後にもたくさん手を加えているので、Markdownの原稿は出版時のテキストとは異なる、古い原稿になっています。

つまり、第1版の内容と完全一致するプレーンテキストはどこにも存在しないのです。
ここが最新のソースコードがプレーンテキストで管理されているシステム開発と書籍制作の大きく異なるところです(注:ラムダノートさんのように、終始一貫してMarkdownで原稿を管理されている出版社さんも中には存在します)。

そこで、編集者さんといろいろ話し合った結果、今回は「第1版のPDFに加筆修正したい内容を指示する」という方法を選択しました。

指示の方法は2パターンあり、加筆修正する内容は分量が少なければPDF内のメモとして書き、まとまった分量が必要な場合は別途Markdownで原稿を書いてGitHubに置く、という方法を採りました。

Ruby 2.4から3.0に変えてね、という指示です
GitHubに置いたMarkdown原稿を使ってください、という指示です
GitHubに置いた原稿の例です

2021年2月〜5月:ガリガリと原稿を書く

2月から5月は加筆修正用の原稿をガリガリと書いていた時期です。
ただ、原稿を書きつつ、いつもどおりソニックガーデンでプログラマとして働いていましたし、フィヨルドブートキャンプでメンターもやっていましたし、あと、この時期にちょうど家のリフォームをしていて結構頻繁にハウスメーカーの担当者と打ち合わせをしていましたし、「やべえ、忙しくて死にそう」という気持ちになっていた時期です😅

自宅のリフォームと並行しながらの原稿執筆はなかなか大変でした……

2021年6月:原稿提出→ゲラの作成開始

いろいろ大変でしたが原稿の執筆はなんとか当初の予定通り、5月末に終わらせることができました。
そこからはいったん編集者さんにバトンタッチして、ゲラ(試し刷り)の作成を進めてもらいました。

編集者さんと共有していた進捗管理表です

2021年7月〜8月:ゲラの校正(1回目)とレビュー依頼

ゲラは7月中旬にできあがりました。PDF版とA3用紙に印刷したものを両方もらって、おかしな内容がないかレビュー(校正)していきます。
自分の書いた原稿を最初から最後まで読み直さなきゃいけないのでなかなか大変です。
でもここでしっかり校正しておかないと、間違いだらけになったり、すごくわかりにくくなったりするので気が抜けません。

こちらが第2版のゲラです

あと、ゲラの初回校正に合わせて、「つよつよRubyプログラマのみなさん」に原稿レビューをお願いしました。
レビューの方法はこちらでざっくりと担当してもらう章をアサインさせてもらい、ゲラのPDFファイルをお渡しして、気になった部分をGitHub issueに報告してもらう、というやり方にしました。

さすが、つよつよRubyプログラマのみなさん、厳しくも的確なレビューコメントが70件も届いて、僕は嬉しい悲鳴を上げながら原稿を修正していきました😂

本当にたくさんのレビューコメントをいただきました🙏

2021年9月:完成に向けたカウントダウン?

9月に入るといよいよ完成に向けた動きが活発になってきます。

カバーデザイン案

まずカバーデザイン案が出てきました。
一見して「プロを目指す人のためのRuby入門」であるとわかることと、一見して第1版とは違っていることがわかることの両立を目指した結果、「表紙のさくらんぼの数を2つに増やす」というデザイン案を採用することになりました。

第2版ではさくらんぼが2つに増えました🍒

ただ、基本路線はすぐに決まったものの、細かい点で「これはどう?あれはどう?」みたいな議論が発生して、最終的にデザインが決定したのは10月末になりました。

2回目の校正

それから、初回校正の内容を反映したゲラが完成したので、2回目の校正を開始しました。
今回もやはり最初から最後まで自分の原稿を読み直していきます。あー、大変、大変💦

索引の作成

そしてこのタイミングで索引を作成していきました。
第1版のときと同じく、今回も「索引に追加したい」と思った用語やキーワードを蛍光マーカーで書き込んでいきます。
さらに今回は同じ記号でも文脈によって意味が異なる場合は、別々の用語として扱うようにしたので、第1版のときよりも細かく指示を入れていく必要がありました。これも超大変💦

たとえば以下は...という記号を「範囲リテラル」として扱う場合と、「引数の委譲」として扱う場合でそれぞれ索引を分ける場合のマーキング例です。

こっちは範囲リテラル
こっちは引数の委譲

これがこういうふうに別々の用語として扱われるわけですね。

読者にやさしい索引を目指しました!
急きょ、デバッガの説明を置き換える

さらに、このタイミングで急きょ本書で使用するデバッガをbyebugからdebug.gemに変更することにしました。
理由はRuby 3.1からdebug.gemが大幅にアップデートされることと、Railsでも標準でインストールされるデバッガがdebug.gemに変更されることが決まったからです。

ただ、ふつうはこのタイミングで内容を大きく書き換えるのは御法度なので、編集者さんには結構無理をお願いしたことになります(編集の吉岡さんに感謝🙏)。

9月の終わりに急きょ差し替えたdebug.gemの原稿です
第2版発売のアナウンス

第2版発売のアナウンスも9月に行いました。
本当はもうちょっとあとでも良かったんですが、「第1版を買った直後に第2版が発売された!悔しい!!」という人があまり出ないよう、「第2版の発売は12月か。じゃあ購入はもうちょっとだけ待とうかな」と思ってもらえるような時期を選んだつもりです。

blog.jnito.com

2021年10月:3回目の校正、まえがき・あとがきの執筆など

10月は3回目の校正作業やまえがき・あとがきの執筆などを行いました。
そうそう、Matzさんに「本書の刊行に寄せて」を執筆してもらったのもこの時期です(お忙しいところ本当にありがとうございます!)。

また、GitHubで公開しているサンプルコード集も10月に作りました。
github.com

通常の書籍制作ではこのタイミングの校正で直せるのは軽微な誤字脱字程度なのですが、自分で読み直すと、「あ、ここがなんかわかりにくい」とか、「このサンプルコードはこっちの方が読者にとって親切かも?」みたいな「直したいポイント」がゴロゴロ出てきて、これまた編集者さんにご無理を言って多数修正してもらいました(ほんと、すいません……)。

2021年11月:入稿、そして発売へ……!!

本書の発売日は2021年12月2日ですが、著者として手を動かすのは11月上旬の入稿(原稿を印刷所に引き渡す作業)までです。
ただ、読み直せば読み直すほど修正ポイントが見つかり、入稿直前まで編集者さんとやりとりを続けて、なんとか無事に入稿にこぎつけました。
編集の吉岡さん、その節は大変お世話になりました🙏

そして、11月24日にとうとう僕の手元に完成した本が届きました🎉

また、東京近辺の大型書店では正式発売日よりも前に先行販売が開始されていたようです。
いやあ、嬉しい。感無量です!

それから、正式発売日の2021年12月2日には、新聞に広告が載ったりもしました。
すごい、びっくり!

以上のようなプロセスを経て、「プロを目指す人のためのRuby入門・改訂2版」は約1年がかりで制作&発売されたのでした。めでたしめでたし👏

改訂版を制作してみた感想

というわけで、このエントリでは去年発売した「プロを目指す人のためのRuby入門 改訂2版」の制作裏話を書いてみました。

感想をひとことで言うなら・・・想像の10倍ぐらい大変でした!!

いや、もっと手を抜こうと思ったら抜けたというか、「単純に古い情報を新しい情報に差し替えるだけ」で終わらせればもっと楽だったと思うんですよ。

ただ、せっかく第2版を出すなら、すでに第1版を持ってる人も「これなら買い直して良かった」と思ってもらえるような本にしたい!というゴールを設定したので、自分の中でハードルがめちゃくちゃ高くなってしまった、という感じです。

いかがでしょう?ご購入いただいたみなさんには満足いただけてるでしょうか??

読者のみなさんの感想と、このエントリのまとめ

Amazonやブクログではこんな感想をいただいているので、筆者としては大変だったけど、頑張って良かったな〜という気持ちになっています😊

j5fisjojeeさん

プログラム初心者というよりも他の言語経験者が最新のrubyを学びたいときの第一の選択肢となる本です。ページ数が多いですが読みやすくて楽しい本なのでrubyやrailsに興味があるなら手に取ることをオススメします。最新の事情に合わせて旧版から加筆されているので是非とも買い替えて読んでほしいです。とにかくオススメの本です。

https://www.amazon.co.jp/gp/customer-reviews/R1D6H0MA8C1IXD/
ふーがさん

第1版を読んで約1年。第2版を読んでみましたが、随所で説明が理解しやすくアップグレードされていたり、新しめの機能であるパターンマッチングや型検査についても解説されていてさらに読み応えがアップしています。また、当時理解できなかったProcやLambdaが理解できるようになったのは、自分自身がレベルアップしたことを実感できて嬉しかったです。また自分が少し成長したときに、改めて読み直したい1冊です。

https://booklog.jp/users/1b2f88f80c73c22b/archives/1/B09MPX7SMY
てけてけさん

個人的には流して読む箇所としっかり読む箇所のメリハリがつけやすい構成になっていて読みやすかった印象です。
既存のコードが読めて、かつ改修できる知識は身につくのでは。
yield の章はこれからも何回も読む気がします。
比較的新しいデバッガや型付けのRBSの話まであってありがたい。
あと、素のRubyとRailsは違うんだぜってのも強調してくれていてそこもありがたかったです。

https://booklog.jp/users/tktk1023/archives/1/B09MPX7SMY

改訂2版をまだ手に取ってない方はこの機会にぜひ!

あと、書評ブログやレビューコメントもまだまだお待ちしてますので、本書の忌憚のない感想をぜひ聞かせてください。

みなさん、これからも「プロを目指す人のためのRuby入門」をよろしくお願いします!

あわせて読みたい

改訂2版っていったいどんな内容なの?第1版とどこが違うの?という話は以下のブログ記事に詳しくまとめてありますので、興味がある方はぜひ読んでみてください。

blog.jnito.com

より具体的な変更点は、シモカワさんがこちらのブログで非常に丁寧にまとめてくださっています(どうもありがとうございます!)。

aim2bpg.com

【動画あり】続・リーダブルテストコード:みなさんからの質問に答えてみました #vstat

前回書いたブログの続きです。

blog.jnito.com

「VeriServe Test Automation Talk No.3」というオンラインイベントで登壇した際に参加者のみなさんから質問をたくさんいただきました。

一部はイベント内で回答したのですが、時間内に全部回答することはできなかったので、ここで回答することにします。
ただし、テキストで回答を書こうとするとかなり大変なので、YouTube動画にしています。

興味深い質問が多数あって、何かしらみなさんの参考になると思うのでぜひ一度ご覧ください😄


www.youtube.com

動画を見る時間がない、という人のために、ざっくりとQ&Aの内容を書いておきますね。

質問1

先日開発が始まって半年くらいのあるプロダクトの開発を引き継ぎました。
ドキュメントが一切なく開発者が私一人です。
テストを作り始めていきたいと思うのですが、まず着手するべことに
ついてアドバイスいただけないでしょうか。

レガシーな巨大リポジトリにテストを追加しようと思って二の足を踏んで
いるのですが、どういったファイルからテストを書いていますか?
また、どうすればレガシーコードにテストを導入しやすいでしょうか?
目的や優先度、テストの書き方など、考慮すべきポイントがあれば教えて
下さい。

ユースケース的に一番重要なE2Eテストから書いていきましょう!

質問2

レビューをする際、機能自体のレビューにかけた時間に対してテストの
レビューにかける時間はどのくらいの割合で行っていますか?

時間的な割合だと10〜30%ですかね〜。

質問3

初心者な質問かと思うのですが、失礼致します。
テストを書いた時に、テストのコード量に応じて仕様変更に対して動きが
重くなると思いますが (テストを沢山直さないといけない)
これは仕方がない事なのでしょうか?何か工夫はありますか?

基本的には仕方ないことですね。
工夫としては「権限だけをテストするテストコード」と「それ以外のテストコード」を分割することがあります。

あと、このツイートも追記しておきます……。

質問4

たとえばバックエンドのAPIを開発する際、エンドポイントごとのテストは
必ず書くのですが それより奥の層(サービスメソッドやリポジトリメソッド)
は必要に応じて、とすることが多いです
本来的にはその奥の層の各部品に対しても、基本的なユニットテストを
整備してあげるのが望ましいとは思うのですが、
優先度としては上記のようにする場合が多いです
みなさんはそのあたりどう考えていらっしゃるでしょうか

その考え方で問題ないと思います!

質問5

RSpecで、例えばuserのroleがadminである、ことがテストの結果として
必要な場合、 let(:user) {create(:user, role: :admin)} のような形で描くか、
before do ... enduser.update(role: :admin)のようにかくか、
あるいはit do ... endの中で

user.update(...)
expect(...)

と書くか、どれが良いでしょうか?
個人的にはbeforeやitの中がわかりやすい気がしていますが、let内で書くのが
今担当しているPJ 内の慣習のようになっているので気になっています。

「テストの結果」ではなくて「テストの事前条件」のことでしょうか?
であればletやbeforeで書くことが多いです。
可読性が一定レベルを超えていれば、どちらでも良いと思います。
テストコードについて、あまり細かいレベルであれを直せ、これを直せというのは時間の使い方がもったいないかも。

質問6

仕様Aから仕様Bに変更した場合、テストはどう変更すると良いかの方針
などありますか? 既存の「仕様Aであること」を確認するテストを
「仕様Bであること」のテストに書き換えるのか、
「仕様Aでないこと」と「仕様Bであること」のテストが別にあった方が
いいのか、など

「仕様Bであること」のテストだけでOKです。
ただ、「仕様Aでないこと」もテストしないと不安になるときは「仕様Aでないこと」のテストも書きます。

質問7

テストコードを再利用したい時に、振舞いとデータに分けて実装したく
なります。 そうなる変数を使いたくなります。
この様な場合、どう対処したら良いでしょうか?

パラメタライズドテスト(データ駆動テスト)を利用するといいかもしれません。
下記ブログ記事の後半にパラメタライズドテストの利用例が載っています。

blog.jnito.com

質問8

変数を使うと、テストコードを変更がいらないというメリットもあると思うのですが、いかがですか?

class Hoge
  NAME = 'suzuki'
  def name
    NAME
  end
end
# 名前が変わったときに変更しないといけない 
it 'NAMEがsuzukiであること' do
  expect(Hoge.name).to eq 'suzuki' 
end
# 名前が変わっても変更しなくていい 
it 'NAMEがtanakaであること' do
  expect(Hoge.name).to eq Hoge.NAME 
end

アプリケーション側の定数は直接参照しない方がいいです!
詳しくはこちらのブログをご覧ください。

blog.jnito.com

質問9

こんばんは。Ruby初学者で、Railsを使ってアプリケーション開発の
勉強をしている者です。
質問なのですが、初学者が陥りがちなNGテストコードあるある等が
あれば教えていただければ幸 いです。

初学者が陥りがちなNGテストコードあるあるといえば、idをベタ書きする人をたまに見かけますね。
「ベタ書き大事」といってもidをベタ書きするのはNGです。これは自動採番される値なので。

# idはベタ書きしちゃダメ!
let(:user) { FactoryBot.create(:user, id: 1) }
let(:project) {
  FactoryBot.create(:project,
    id: 1,
    name: "RSpec tutorial",
    user_id: 1)
}
let!(:task) { FactoryBot.create(:task, project_id: 1, name: "Finish RSpec tutorial") }

Railsであればモデルの関連をうまく活用すればidをベタ書きせずに済みます。

# 関連を活用すればidをベタ書きする必要はない
let(:user) { FactoryBot.create(:user) }
let(:project) {
  FactoryBot.create(:project,
    name: "RSpec tutorial",
    user: user)
}
let!(:task) {
  project.tasks.create(name: "....")
}

ちなみに上記のコードは「Everyday Rails - RSpecによるRailsテスト入門」のサンプルコードを一部改変したものです。

leanpub.com

質問10

システムテストを統合テストより先に書くべきでしょうか??
Railsのminitestを使っていて、最もエンドユーザの行動に近いブラウザを
使ったシステムテスト を先に書き、時間があればintegrationやcontroller,
modelのテストを後に書くようにしていま す。

システムテスト(E2Eテスト)が優先、で良いと思います。
僕がテストを書くときもシステムテストかモデルのテスト(システムスペックかモデルスペック)が9割以上です。

質問11

カバレッジについての指針は何かありますか

カバレッジ率については80%以上を目指すようにしています。
100%を狙うのは費用対効果があまりよくないので無理に目指さなくて良いです。
大きな機能をリリースする前は具体的にどのコードがテストされていないのかをレビューすることもあります。

まとめ

僕が回答した質問は以上です。詳しい内容はこちらの動画をどうぞ〜!


www.youtube.com

あわせて読みたい

勉強会本編の登壇内容についてはこちらのブログにまとめてあります。
blog.jnito.com

同じイベントに登壇していた風間さんもいくつか同じ質問について回答してくれているので、こちらも参考になると思います!
nihonbuson.hatenadiary.jp

過度なDRYは読みやすさの敵!?「リーダブルテストコード」という発表をしました #vstat

先日、このブログでもお伝えしましたが、「VeriServe Test Automation Talk No.3」というオンラインイベントで登壇してきました。

veriserve-event.connpass.com

申込者数はなんと1000人を超えていて、大変驚きました。

僕は「リーダブルテストコード」というテーマで発表しました。スライドはこちらです。

Twitterでたくさんシェアされたり、はてなブックマークがたくさん付いたり、こちらもすごい反響でビックリしました。

で、どんな内容だったの?

ひとことで言うなら「テストコードを徹底的にDRYにしようとしちゃダメよ!」というお話です。
このネタは昔からQiitaやTwitterとかでことあるごとに話してきましたが、この勉強会であらためてなぜダメなのか、DRYに書かず、どう書くべきなのか、という話を力説してみました。

優秀なプログラマほど、「DRYが善」と信じて止まないので、テストコードに対してもDRYを追求しようとしてしまいがちです。
今までいろんな人のテストコードをレビューしてきましたが、僕が初めてコードレビューした人の9割ぐらいがテストコードをDRYに書こうとしていたように思います。
まあ、「コードを見たらDRYにしたくなる」というのは、ある意味プログラマの職業病みたいなものなので仕方ない面もあるのですが。

とはいえ、それだといつまでたっても読みやすいテストコードは書けません。
読みやすいテストコードを書くためには、「あえてDRYを捨てる」という発想の転換が必要です。

こういう考え方が世の中のプログラマにもっと広がってほしいな〜と思って、今回のような発表内容に至りました。

他の登壇者のみなさんのスライド

僕の他にも末村 拓也さんと風間 裕也さんが、同じく「リーダブルなテストコード」について、非常にためになる発表をされていました。
こちらもぜひご覧ください。


質疑応答&パネルトークで話した内容

勉強会の後半は登壇者3人とモデレータによる質疑応答&パネルトークでした。
いろんなQ&Aのネタがあったのですが、その中から1つだけ僕が話したトピックをここに書いておきます。

Q. アプリケーション側に定数があった場合、その定数をテストコード側でそのまま利用しても良いか?

定数にもいろんな定数がありますが、ここでは一例として消費税の税率を定数化していた場合を考えてみます。

たとえばこんな感じですね。

class Product
  TAX_RATE = 0.1
  # ...
end

テストコードは以下のようにProductクラスで定義した定数を参照しています。

example '税込み価格が返る' do
  product.price = 1000
  expect(product.price_including_tax).to eq \
    product.price * (1 + Product::TAX_RATE)
end

このテストコードを評価する場合、良い・悪いでどちらかひとつ選べと言われたら、僕は「悪い」を選択します。
僕だったらテストコードはこんなふうに税込み価格をベタ書きします。

example '税込み価格が返る' do
  product.price = 1000
  expect(product.price_including_tax).to eq 1100
end
「なぜ悪いの?」

テストコード内で定数Product::TAX_RATEを参照した場合、将来もし消費税率が15%に変わっても定数の値を変えるだけで修正が終わります。テストコードには何も手を加える必要がありません。

一見、これはDRYであることのメリットのように思えますが、実はデメリットになる可能性も大いにあります。

アプリケーション側の仕様変更に合わせてテストコードが仲良く二人三脚で歩いて行くようなコードを書くと、アプリケーション側の実装にバグが埋め込まれた場合にもテストがパスしてしまう可能性があります。つまり、バグを検知すべきテストコードがやすやすとバグを見逃す可能性があるわけです。これは致命的な問題です!

class Product
  # 寝ぼけたプログラマが消費税率15%をそのまま15としてしまった!
  TAX_RATE = 15
  # ...
end
example '税込み価格が返る' do
  product.price = 1000
  # 税込み価格が1万6000円になってるのにテストがパスしてしまう!!
  expect(product.price_including_tax).to eq \
    product.price * (1 + Product::TAX_RATE)
end
ベタ書きした方がバグを検知しやすい

一方、expect(product.price_including_tax).to eq 1100のようにベタ書きしてあれば、定数を変更したときにテストが失敗します。
そして、次のようなテストコードに書き替えれば、TAX_RATE = 15という設定値が間違いであることにも気付くことができます。

example '税込み価格が返る' do
  product.price = 1000
  # TAX_RATE = 0.15 でなければこのテストは失敗する
  expect(product.price_including_tax).to eq 1150
end

こうした理由から、テストコード内でアプリケーション側の定数を参照するのは避けた方が良い、と考えることができます。

「でも、DRYにしないとテストコードの修正が大変なんです!」

もちろん、テストコードはDRYではなくなるため、仕様変更が発生すると大量のテストが失敗することもあります。ただ、それはデメリットではなく、仕様変更のインパクトがテストコードのおかげで可視化された、と好意的に捉えるようにしましょう。

ただし、「これはある程度DRYにしておかないと、今後のテストコードの保守がかなりキツい」という場合は「ほどほどのDRY」は許容しても構いません。
そういうケースでは僕はrspec-parameterizedというgemを使って、パラメタライズドテストを書くことが多いです。

github.com

Twitter上のみなさんの反応など

ツイートを見る限り、おおむね好評だったようでほっとしました😄

なお、当日の参加者のみなさんのツイートはこちらにまとめられています。
togetter.com

おまけ

いつものことなのですが、登壇前には何度もリハーサルして時間配分やトーク内容を洗練させていっております。

僕が登壇前にどんな準備をしているのか知りたい方は、こちらのエントリをご覧ください。
blog.jnito.com

まとめ

というわけで、このエントリでは先日登壇した「VeriServe Test Automation Talk No.3」というオンラインイベントの登壇内容を紹介してみました。
もっと読みやすいテストコードを書きたい!という人はぜひ今回の登壇内容を参考にしてみてください。

最後に、このイベントに参加してくださったみなさん、登壇者のみなさん、そして運営者のみなさん、どうもありがとうございました!
久々のオンライン登壇でしたが、みなさんのおかげで楽しく発表することができました😄

あわせて読みたい

勉強会当日にいただいた質問に一通り答えてみました。こちらもあわせてどうぞ!

blog.jnito.com

PR:RSpecの本とか、Rubyの本とか

この講演を聞いてテストコードに俄然興味が沸いてきた!という人は、ぜひ「Everyday Rails - RSpecによるRailsテスト入門」をどうぞ。
RSpecを使った実践的なテストコードの書き方が学べます。
leanpub.com

また、「プロを目指す人のためのRuby入門」でも例題を解く際にテスト駆動開発のスタイルを取り入れてます(使用しているテスティングフレームワークはminitestです)。
Rubyと同時にTDDを学びたい方はこちらもぜひ!