日々の記録。

プログラミングのメモや感じた事などを記録。

Kent beckのテスト駆動開発を読んで

読んだ本

著者: Kent beck タイトル: テスト駆動開発 第2版

読んだ動機

色々なアジャイルの本(例えばアジャイルサムライやエクストリームプログラミング)で「自動テスト」が出てくるので、改めてKent beckの原典を読んでみようと思った。

TDDについての情報

Kent beckは、エクストリームプログラムを世に出した人。xUnitの最初のsUnit(Smalltalk unit)の作者の一人。 TDDは「テスト駆動開発」の略。読む前のイメージは「自動テストを書いてから実装する」・・・それだけ。

読み進めるにあたり関心のある点

  • TDDの目的
  • テストコードの管理について
  • 他人のテストコードについて
  • GUIのテスト、DBのテスト,マルチスレッドなどの非同期や並列処理のテスト(あったらいいな程度で期待はしていなかった)

こんなところに興味を持って読んでみた。

得られた知見

TDDの目的

この本から感じたことは、TDDはテストにあらず。実装を早く行い設計を継続的に改善するための開発手法。いわゆる「製品のテスト」との代替ではないということを感じた。

実際、書籍には以下のように書かれている。

TDDはテスト技法ではない(Cunninghamの公案)。 TDDは分析技法であり、設計技法であり、実際には開発の全てのアクティビティを構造化する技法なのだ。(p278)

しかし、p188にはこんな記述もある。

どうやってソフトウェアをテストすれば良いのだろう〜自動テストを書こう。

‥‥

TDDの手法

before TDD

昔々の20年近く前の開発・・・C言語で開発をしていた頃を思い出すと次のステップになる。

  1. 数百行のソースコードを書いてコンパイル
  2. さらにx倍のコードを書いて機能を一通り書く。コンパイル
  3. さあ動作確認
  4. デバッグ
  5. 修正 (以下3~5をエンドレスで繰り返す)

「テストをしていない関数」を使って「大きな機能」を実装して、さらに「そのテストをしていない大きな機能」を使って別の機能を実装する・・・当然バグが出まくる。デバッグに時間がかかる。そんな環境を知っている。

after TDD

TDDの手法は上の方法の真逆を行く。 (テストコードではない、製品やサービスのためのコードを「製品コード」とします)

  1. まずはテストを1つ書く
  2. 全てのテストを走らせ、新しいテストの失敗を確認する
  3. 小さな変更を行う(意訳:製品コードを書いたり変更したりする)
  4. 全てのテストを走らせ、全て成功することを確認する
  5. リファクタリングを行って重複を除去する (p1)

私がこの本を読んで最も大切と感じたことは「テストコードを使って製品コードを細かく動かすことで、「動作していない製品コード」をできるだけ無くす。」ことだ。動かしていないコード(私はこれをよく「絵に描いた餅」と言っている)ではなく、「動くコード(テストをpassしたコード)を使って「より大きな機能の実装」に取り組む」ことができる。(書きかけ。)

次に印象に残った事は、TDDの流れでは「リファクタリングを行って重複を除去する」というステップが伴っていること。私の経験上では、実際のプロジェクトでは1~5の手順に乗っ取るのではなく3,4のみのものばかりに遭遇する(実際には4もない)。設計もなく設計の見直しもなく3ばかりだったら、そのプロジェクトはやばい兆候だと思う。(書きかけ)

テストコードの管理について

私自身が感じているテストコードについての課題として、「プロダクトコードを複数人が入れ替わりで触っている環境で、今はいない誰かが書いたコードをメンテナンスしないといけない状況になった時、存在するテストコードをどこまで信じて良いのだろう? テストがpassしている事は分かる・・・けどそもそも内容も把握できていない何百のテストを信じて良いのだろうか?」という課題がある。それに対してヒントがあるかと思ったけど、やっぱり記載がなかった。

これは私の所感になるけど、結局テストコードを適切に維持するには、テストコード以前に、製品コードが適度にモジュール化されていて(クラスでもライブラリでも言い方はなんでも良い)、そのモジュールの責任範囲が明確になっていて、その責任範囲に置いて「これをテストする」という事が明確になっていなければ、テストコードの維持管理はできないのではないだろうか?

今携わっている環境で感じる事は、

  1. 数百のテストコードが何をしているか分からない
  2. そもそもテスト対象のクラスの責任範囲が分からない(1クラスが数千行のコードからなる)

つまるところモジュール化が行われていない環境でテストコードを作成していても、テストコードの管理はほぼ不可能と感じている。(そもそも製品のコードが管理できていないのに、テストコードの管理は時期尚早なのかもしれない)

この本への不満

  • 著者の暗黙知による話が多い
    • 本文中には、「目指すのは動作する綺麗なコード」「より良いコードを書けば、よりうまくいく」など「綺麗なコード、良いコード」についての言及があるが、具体的にどのようなコードが「良い」なのか、この書籍だけでは分からないと感じた。
    • この本の1部はTDDを使った開発の例となっているが、話の展開が、著者の経験や設計論の暗黙知に依るところを感じ分かりづらいと感じた。
    • 「パターン」という章があるけど、パターンというより単なるエッセイになっている。特にパターンに対する定義付けがなく、内容が分からない箇所があった。
      • 例 「三角測量」の項目で、文章中で「2つ以上の例がある時だけ一般化しよう」という記載があるけど、「〜という方法を三角測量という」といった定義付けがなく、読み手の推測で判断する箇所があった。
      • 例 デザインパターンとして出てくる「Imposterパターン」・・・「既存オブジェクトと同じプロトコルを備え、実装は異なる新たなオブジェクトを作ろう」とあるけど、これのどのあたりがデザインパターンとして分類される内容なのか、またImposterパターンとはなんなのか、私の読解力では理解できなかった。(そもそもデザインパターンは「既存オブジェクトと同じプロトコルを備え、実装は異なる新たなオブジェクトを作ろう」これを基礎とした設計カタログではないかと思うのだけど)

感想

身もふたもないけど、この本を読むならリファクタリングを読んだ方が良いと感じた。 理由としては、

  • リファクタリングの方が、暗黙知が少なく(個人的には)読みやすい
  • 「小さく書いて小さく動かす」というTDDとの共通点もリファクタリングの本に含まれている
  • カタログが1つ1つ簡潔にまとまっていて体系立っている
  • 良いコード、悪いコードの事例として参考になる

などなど、リファクタリングの方が得られることが多いのではないかと思った。

もう一つ感じた事は「テストコードの管理は・・・」について気になっていたけど、そもそも「製品コードのモジュール化」がなければ、テストコードの管理も何もないのでは、とこの記事を書きながら気づいた。

おまけ

アジャイルの書籍で必ず「自動テスト」という項目を見かけるけど・・・、TDDでのテストケースは「テストではなく開発の技法」という内容が記憶に強く残っている。 これらの本での自動テストの説明は、簡略化されまた美化されすぎている気がする。

経験からの「認識の違い」のいくつか

「選択の科学」から

「選択の科学」という本に、”旧共産圏で暮らしていた人は、沢山の種類の(例えば7つの)炭酸飲料があっても、「炭酸ならどれも同じ、選択肢は1つ」という認識になる傾向になる”というエピソードがある。

旧共産圏の人は、選択肢は少ないが物が平等に手に入るが、「多様な商品の中から選択する」という経験を積んで来なかった為に、こうした傾向になるらしい。(シーナ・アイエンガー「選択の科学」)

例えば、次の画像を見せられて「どの灰色が好き?」と言われても、美術に疎い自分には「どの灰色も同じでしょ」となる。そんな事と似ているのかもしれない。

f:id:hmu29:20170618103908p:plain

ドラクエコンサートから

先日、東京交響楽団によるドラクエのオーケストラコンサートに行ってきた。自分としては、ドラクエ世代ということもあって、とても楽しい演奏会だった(プログラムにいくつか不備があり、演奏とは関係ないところで残念なことはあったけど・・・)。

twitterでこのコンサートの感想を見てみたら、「良かった」という声の他に、リハ不足などの演奏の質についての言及がいくつかあった。こういう方々たちは、自らオケで演奏をするような人だったり、よく演奏会に足を運んでいて、耳が肥えてる人たちなのかもしれない。

脳は、最初は大まかにしか物事を捉えることができず、訓練を積むことで細かいことも分かるようになるようだから(池谷裕二 「記憶力を強くする」)、やはり彼らは耳が肥えていたのかもしれない。

ソースコードから

仕事で人が作成したソースコードを眺めていると、コピペをしたのか分からないけど、複数のメソッドがほぼ同じ・・・ということに出くわすことが稀にある(たまに?時々?)。自分には、これは”ほぼ”ではなく、同じ処理という認識になるのだけど、もしかしたら書いた人にとっては、”ほぼ同じ”ではなく、”全くの別物”という認識なのかもしれない。「選択の科学」を読んで、そんなことを思った。

リファクタリングという言葉一つとっても・・・

リファクタリングという言葉一つとっても、人によって解釈が異なってしまうのでは? というお話。

自分が勤めている職場は、中途入社が多い会社で、色々な経験や知識を持っている人が集まっている。

そんな環境で仕事を進める時には、「”自分が持っている知識と経験”と”相手の持っている知識と経験”は一緒ではない」という事実を忘れない事が大切だと感じている。

例えば「リファクタリング」という言葉一つとっても

ある人には

「他人がその言葉を使っているのを聞いた事がある。なんかコードを綺麗にする作業でしょ」という認識かもしれない。

ある人には

「コードの重複を省き、共通化する作業」という認識かもしれない。

ある人には

「書籍「リファクタリング」を読んだ事があり、体系化された手続きを理解していて、単体テストが必要で、マルチスレッドに対してのリファクタリングには注意が必要」という認識かもしれない。

ある人は

リファクタリングとそのゴールの一つにデザインパターンがある事を知っていて「デザインパターン」を読んだ事がある」かもしれない。

ある人は

「機能追加と同時にコードの整理を行う」かもしれない。

自分が言うまでもなく、「リファクタリング」と言う言葉一つをとっても、人によって理解、解釈は異なっていると思う。そして人に対して、どう解釈しているかを確認する機会はあまりない。(自分がどう理解しているか、を認識する事もあまり無いかもしれない。)

仕事で関わる人には様々な人がいる。「プログラミングに興味なく、生活の為に仕事をしている」「プログラミングが好きで仕事に積極的な人」などなど。そんな人全員に、「リファクタリング」の本を読めとか、「リファクタリングはこう言うものだ」と押し付けることはあまり効果は無いと思う。

だから仕事上では、「正しい定義や知識」が最重要ではなく、「なぜそれを行うか」「具体的に何を行うか」「行なった結果どうなるか」「何を行わないか」という事を、関わる人が共通認識し、作業に納得する事が大切ではないかと思う。

自分への戒めとして書き留めておく。

上で述べた事とは異なるけど、言語仕様や製品ドキュメントについては、せめて一次ソースを当たる事が大切だと思う。「言語仕様とリファクタリングの差が何か」を説明するには、まだ自分の中でまとめきれていない。仕様として規定されているか、思想かの違いか・・・?

レビューするとき、されるとき

先日、チームメンバーが作成した仕様をレビューする機会があった。 相互理解を行う機会なので、認識の不一致は当然として、その他に記載漏れがあったり、誤字、コピペミスがあったりと、色々と実りある時間だったと思う。

その日の帰りの電車で、池谷裕二さんの「脳はなにげに不公平」という本を読んでいたら、次のようなエピソードが紹介されていた。

” 物を部屋に隠してもらう/探してもらう実験において、実験の参加者が物を隠す時は、見つけづらい場所に隠すけど(部屋の真ん中付近)、その隠した人が探す側にまわると、探しやすい場所から探す(部屋の端から探し始める)” というもの。隠す側は「いかに相手の裏をかこうか」熟考する一方、探す側は「いかにも」な場所を選ぶらしい。(隠すとき、探すときで、脳の働く部位が異なるらしい。興味のある方は、本を買って確認してみては。)

そのエピソードを読んで、「”仕様を書く”(そもそも「書く」なのか「作る」なのか)、”仕様を読む”(そもそも「読む」なのか「理解」なのか)という行為も、脳の使う部位が異なり、自分では客観視しづらいのではないだろうか?」と思った。 客観視しづらいのであれば、能力をあげるには、書きっぱなしにするのではなく、書いた上で、良いレビューを受けることが大切なのかもしれない。

この文章は、当然書きっぱなしなので、わかりづらい事は十分あり得る。

iPadより紙が良い/本を使う

iPadより紙が良い

これが、iPad2を利用して6年、iPadiPhone電子書籍を読み続けた中、私が得た教訓。 (と言ってもiPad2はここ2,3年ほとんど使っていない)

「どんな本も紙でなくてはダメ」というわけではなく、マンガ や小説のように、ストーリを楽しむ娯楽的な本は電子書籍で十分で、これからも電子書籍で買い続けると思う。(家族に怒られずにマンガを読めるし・・・。) 一方、コンピュータなどの技術書やビジネス書など、「本を読む」というより「本を使う」の場合まだまだ紙が良い

「本を使う」とは今思いついた表現で(すでに使われているかもしれないけど)、本から積極的に、知識や情報を得たり、考えや疑問を上げながら本を読むこと。私にとっては「線を引く」「疑問や感想を書き込む」「付箋を貼る」「読み返す」という作業が伴った読み方になる。

本を読み、ただ知識を得て「はい、終わり」ではなく、本を読んでいる過程で考えたことが重要ではないかと個人的に考えていて、「本を使う」ことには、まだまだ紙の方が勝っているように感じている。

「本を使いたい」から、しばらくは紙の本を買い続けると思う。・・・もしかしたらiPadが悪いのではなく、「本を使う」こと向けの電子書籍リーダーの誕生が待ち望まれているのかもしれない。

Podfileで取得するプロジェクト一覧を出力する

ポイント

#!/usr/bin/env ruby

def pod(name, option = {}) 
  puts name
end

def platform(value, ver)
end

podfile = open('./Podfile', &:read)
eval podfile

Xcode ターミナルから楽して開く

インストール

cd ~
git clone https://github.com/hmuronaka/xcode_scripts.git
cd xcode_scripts
./install.sh

.bash_profileに以下を追加

source ~/.xcode_scripts/xcode_script.bash

出来る事

xo (xcopen) ... カレントディレクトリ以下のXcodeプロジェクト名を指定してXcodeを開く。 xc (xccd) ... カレントディレクトリ以下のXcodeプロジェクトのディレクトリに移動する。

例として、次のフォルダ構成にXcodeプロジェクトがあるとします。

~/src/xcode
       |- plugin
       |    |- XVim
       |    |    |- XVim.xcodeproj
       |    |- MCLog
       |         |- MCLog.xcodeproj
       |- lib
       |     |- Logger
       |     |    |- Logger.xcodeproj
       |     |- MyUI
       |          |- MyUI.xcopdeproj
       |          |- MyUI.xcworkspace
       |- App
       |     |- MyApp1
       |          |- MyApp1.xcodeproj
       |          |- MyApp1.xcworkspace
       |- temp
             |- Logger
                  |- Logger.xcodeproj           
 

xo (xcopen)

cd ~/src/
xo XVim # ~/src/xcode/plugin/XVim/XVim.xcodeprojを開く
xo MyUI # ~/src/xcode/lib/MyUI/MyUI.xcworkspaceを開く.xcworkspaceがある場合は、それを優先する。

xo My # ここでTabを押すと補完が表示される
MyUI MyApp1

xo # プロジェクト名無しの場合は、履歴を表示する
0: XVim: ~/src/xcode/plugin/XVim/XVim.xcodeproj
1: MyUI: ~/src/xcode/lib/MyUI/MyUI.xcworkspace
select a path >  # 0または1を入力する。qを入れるとキャンセルする。

xo Logger # 複数ある場合は、パスを選択する
0: ~/src/xcode/lib/Logger/Logger.xcodeproj
1: ~/src/tmp/Logger/Logger.xcodeproj

cd App/MyApp1
xo . # カレントディレクトリにあるXcodeプロジェクトを開く

xc (xccd)

xccdはxcopenのcd(pushd)版です。

cd ~/src/
xc XVim # ~/src/xcode/plugin/XVim/に移動する.
popd

xc MyUI # ~/src/xcode/lib/MyUI/に移動する。
popd

xc My # ここでTabを押すと補完が表示される
MyUI MyApp1

xc # プロジェクト名無しの場合は、履歴を表示する
0: XVim: ~/src/xcode/plugin/XVim/XVim.xcodeproj
1: MyUI: ~/src/xcode/lib/MyUI/MyUI.xcworkspace
select a path >  # 0または1を入力する。qを入れるとキャンセルする。

xc Logger # 複数ある場合は、パスを選択する
0: ~/src/xcode/lib/Logger/Logger.xcodeproj
1: ~/src/tmp/Logger/Logger.xcodeproj
select a path >