Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
テスト駆動開発試してみた発表
Search
tarohida
June 10, 2020
Programming
280
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
テスト駆動開発試してみた発表
自作のTwitterbotアプリのリファクタリングついでに、TDDを実際に取り入れてテストコードを書いてみました。
その際得られた知見を共有します。
tarohida
June 10, 2020
More Decks by tarohida
See All by tarohida
リモートワークをきっかけに、見積もりと進捗について考えてみた
tarohida
0
44
事故について考えてみようの会
tarohida
0
210
Other Decks in Programming
See All in Programming
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
380
[ハンズオン]AIへの指示だけで「五目並べ」を作ってみよう
satoshi256kbyte
1
380
Rでドレミ/Do-Re-Mi_with_R
florets1
0
130
Railsのためのパスキー実践入門 〜マルチテナントSaaSでの意思決定〜
utakaha
2
650
気持ちぃ〜角丸 Squircle
ryu1013
0
310
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
月間100万人のサービス刷新で、 技術選定をHonoに振り切った話
yutobara
8
6.7k
JAWS-UG 東京支部が始める、JAWS-UG支部コラボ / JAWS-UG lunchtime LT Collaboration
y0hgi
0
280
作って学ぶSpring BootのAuto Configuration / JJUG Night Seminar 2026-10-19
rshindo
0
160
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
300
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
370
作れる、でも消せるか?──安全に「枯らして消す」機能削除術 / Drain, Then Delete
chobishiba
2
770
Featured
See All Featured
Thoughts on Productivity
jonyablonski
76
5.4k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
Darren the Foodie - Storyboard
khoart
PRO
4
4k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.4k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
310
How to make the Groovebox
asonas
2
2.5k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
460
HDC tutorial
michielstock
2
960
Building a Scalable Design System with Sketch
lauravandoore
464
34k
How to build a perfect <img>
jonoalderson
1
6.1k
Git: the NoSQL Database
bkeepers
PRO
433
67k
Transcript
テスト駆動開発試してみた t-hida
TDDについての説明 以下の資料が分かりやすかったので、概要については以下の資料に基づいて説明しま す。 https://gihyo.jp/dev/serial/01/tdd/0001
テスト駆動開発とは? 単にテストを書けばテスト駆動開発ではない
テスト駆動開発とは? 「テスト駆動開発」p.298 「訳者解説:テスト駆動開発の現在」より > テストコードを書くプログラマが増えるに従って、いくつかの問題が出てきてしまいまし た。具体的には、テストコードを書くことが形骸化、あるいは自己目的化してしまい、テス トの価値や目的があやふやになってしまったり、脆いテストコードや遅いテスト、不可解 な失敗の仕方をするテストが、プロジェクトのメンテナンスコストを増大させたりしていま した。 またTDDについても、テストを先に書くことがTDDであるという誤解が生じていました。
テスト駆動開発とは? 小さなフィードバックループを回していくことによって、開発を効率的に進めていくのがテ スト駆動開発
テスト駆動開発とは? どのようにフィードバックループを回すのか? -> テスト駆動開発の3ステップ
テスト駆動開発とは? テスト駆動開発の3ステップ • ステップ1:これから書く機能に対するテストを1つ書き,テストが失敗することを確認 する(レッド) • ステップ2:ステップ1のテストを通す最低限のコードを実装する(グリーン) • ステップ3:リファクタリングを行う(リファクタリング) https://gihyo.jp/dev/serial/01/tdd/0002より
テスト駆動開発とは? この3ステップについては、後ほど実際にコーディングしてみます。
テストとは? 一般的な分類 • 「ユニットテスト」「 単体テスト」 • 「 機能テスト」「 結合テスト 」
• 「システムテスト」 https://gihyo.jp/dev/serial/01/tdd/0004
テストとは? 説明のため3種類に分類してみる (t-wada独自) - Developer Testing:開発者用 - Customer Testing:進捗管理用 -
QA Testing:品質管理用 TDDで用いるのは、開発者が開発者のために書くテスト、フィードバックを得るためのテ スト、Developer Testingとここでは位置づける https://gihyo.jp/dev/serial/01/tdd/0003
テストとは? 説明のため3種類に分類してみる (t-wada独自) - Developer Testing:開発者用 ←テスト駆動開発の対象範囲 - Customer Testing:進捗管理用
- QA Testing:品質管理用
Developer Testing > 先述したように,テスト駆動開発におけるテストというのは,品質のためのテストでは ありません。テスト駆動開発でのテストの位置づけは,開発を先に進めて行くための「は ずみ車 」 ,開発を前に進める「原動力」です。 > 自分が書いたコードは自分でテストします。そして,テストを書くことで,フィードバック
を得ることができます。「 次はこうしなきゃいけないはずだ」「 こういうコードのほうがいい んじゃないか」――テストがもたらしてくれる情報をもとにコードをより良くしていこうという のが,テスト駆動開発におけるテスト,つまりDeveloper Testingの役割です。 https://gihyo.jp/dev/serial/01/tdd/0003
実際にテスト駆動開発の3ステップをやってみます https://github.com/taro-hida/testcace_practice
テスト駆動開発をしばらく取り入れてみて メリット - メソッド内部のリファクタリングがしやすくなった テストケースに通ったあとは、引数と返り値を変更しないということさえ守れば心置 きなくリファクタリングができる
テスト駆動開発をしばらく取り入れてみて メリット - var_dump() でのデバッグがしやすくなった 実行単位がテストケース単位 (=メソッド単位)となるので、エラーが発生した際に var_dumpを仕込むのが安易 (実行もCLIなので、エラー出力による確認もしやすい) テストコードから更に問題を分割して、マイクロプログラムを作成して挙動を確認す
ることも容易
テスト駆動開発をしばらく取り入れてみて メリット - エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなっ た - これは、すごく小さいループを回した結果得られた効果 > 失敗するテストを書いてから、最初に行う実装はどのようなものだろうか ―
ベタ書きの値を返そう。それでテストが通るようになったら、ベタ書きの値をだんだん本物の式や変 数に置き換えていく。 >「テスト駆動開発」Kent Beck著 和田卓人訳 P.217 「グリーンバーのパターン」
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった 効果を実感したテストケース /** * @depends testConstruct */
public function testCreateMessage(OnanieCounter $onanieCounter) { $user_id = '12121212121212121'; $screen_name = 'taro'; $user_name = 'Taro'; $this->assertSame("@taro Taroさんの一日あたりのツイート数は0.25回です\n, tweet_counter->createMessage($user_id, $screen_name, $user_name)); }
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった テストケースに対応するメソッド public function createMessage(string $user_id, string
$screen_name, string $user_name): string { $tweet_count = $this->getTweetCount($user_id); return "@{$screen_name} {$user_name}さんの一日あたりのツイート数は{$tweet_count}回です\n"; }
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった 最初から全部書いてしまうと、どこがおかしいのかわからない - 自分の書いたテストケースがそもそもおかしいのか - クラス名、メソッド名、ネームスペース等をタイポして読み込めていないのか -
returnする値が間違っているのか - 変数名が間違っているのか - 外部メソッドの呼び出し方が間違っているのか - 外部メソッドが返した値がおかしいのか
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった 最初から全部書いてしまうと、どこがおかしいのかわからない -> メソッドの返り値をベタ書きから始めると、どの変更によってエラーが発生したのか、 わかる!
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった テストケースに対応するメソッド public function createMessage(string $user_id, string
$screen_name, string $user_name): string { $tweet_count = $this->getTweetCount($user_id); return "@{$screen_name} {$user_name}さんの一日あたりのツイート数は{$tweet_count}回です\n"; }
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった まずベタ書きする -> この状態でエラーが出た場合は、クラス名やテストケースの書き方、 テストケースでの初期化処理に問題がある。 public function
createMessage(string $user_id, string $screen_name, string $user_name): string { $tweet_count = $this->getTweetCount($user_id); return "@taro Taroさんの一日あたりのツイート数は0.25回です\n,; }
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった 変数を移動する -> この状態でエラーが出たときは、変数の受け渡しに問題がある。 public function createMessage(string
$user_id, string $screen_name, string $user_name): string { $tweet_count = $this->getTweetCount($user_id); return "@{$screen_name} {$user_name}さんの一日あたりのツイート数は0.25回です\n"; }
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった 内部的に呼び出しているメソッドの返り値を移動する -> この状態でエラーが出たときは、メソッドおよび、メソッドへの値の引き渡し方に問題がある public function createMessage(string
$user_id, string $screen_name, string $user_name): string { $tweet_count = $this->getTweetCount($user_id); return "@{$screen_name} {$user_name}さんの一日あたりのツイート数は{$tweet_count}回です\n"; }
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった このように、(今回の例は細かすぎるけれども)問題を細かく分割して考えられるのが、 テスト駆動開発の手順を踏むことによるメリット public function createMessage(string $user_id,
string $screen_name, string $user_name): string { $tweet_count = $this->getTweetCount($user_id); return "@{$screen_name} {$user_name}さんの一日あたりのツイート数は{$tweet_count}回です\n"; }
テスト駆動開発をしばらく取り入れてみて メリット > エラーが出た際、自分のどの作業によってバグが発生したのかがわかりやすくなった 副次的なメリット - コーディングにおいて、一歩一歩確実に進んでいく実感が得られた 今回であれば... 「引数を移動して、テストがパスした」 ->
残りの作業は、内部的に呼び出したメソッドの返り値を、テスト対象のメソッドの 返り値に受け渡すだけ
テスト駆動開発をしばらく取り入れてみて 難しいと感じた点 一言で言えば、「設計が難しい!」と感じました。
テスト駆動開発をしばらく取り入れてみて 難しいと感じた点 > - リファクタリングをする過程において、他のメソッドの呼び出し方や、取る引数を変 更したくなる 割と頻繁に既存のテストを何度も何度も書き換えることになる いったり来たりして結構めんどう
テスト駆動開発をしばらく取り入れてみて 難しいと感じた点 > - ひとつのメソッドのためのひとつのテストを書いているうちに、そのメソッドのために 必要なもう一つのメソッドを書く必要があることに気づく 例:リプライ元に、”あなたの今日のツイート数は◦◦です”と返答する -> よくよく考えると、その文章を作成するためのメソッドが必要だな ->
並行して2つのメソッドを考え始めるので、脳みそがしんどくなる
テスト駆動開発をしばらく取り入れてみて 難しいと感じた点 > - コードの構造を考えるにおいて、テストを書いていなかった頃とは異なるアプローチ が必要になった 今まで:挙動と実装、内部処理から組み立てていく テスト駆動:満たすべき要件とテストから組み立てていく 今までと同じ手法で取り組むと、アホほどテストケースを書き直す必要が発生する。 ある程度考えてから書き始めないといけない。
テスト駆動開発をしばらく取り入れてみて 気づいた点 > - インスタンスに状態をもたせると、テストを実施しにくくなる - 同じことを言っていた記事 > このとき、悪性の Singleton
が入力にあると、テストコードから Singleton を意図した状態へもっていく必要がでてき ます。すると、テストコードの実行前に Singleton の状態に応じた分岐を書く必要がでてきます。これはテストの本質と は関係のないコードであるため、テストコードの記述量を余計に増やす上に、見通しをとても悪くさせます https://qiita.com/Kuniwak/items/4314451227f4d5eaa6b8#%E8%87%AA%E5%8B%95%E3%83%86%E3%82%B9%E3 %83%88%E3%81%8C%E3%81%AA%E3%81%84%E3%81%A8%E4%BD%95%E3%81%8C%E8%B5%B7%E3%81% 93%E3%82%8B%E3%81%AE%E3%81%8B - また、インスタンスを生成するまでの事前準備の処理がすごく長くなる
テスト駆動開発をしばらく取り入れてみて 気づいた点 > - テストケースによるDB操作は、そこまで時間がかからなかった - ただし、DB操作をテストに盛り込むのはアンチパターン - TDDにおいては、DBから値を直接取り出すのは NG、テストコードから直接サンプルの値を引き渡
すのが望ましい - 速度だけ考えれば、 DBから引っ張ってきた値で代用することはできそう 今回:sqlダンプで本番環境から吐き出した sqlを手元dockerのpostgres環境に流し込んで、本番環 境で利用していたレコードをテストでも利用 https://openmoji.org/library/#search=thinking&emoji=1F914
テスト駆動開発をしばらく取り入れてみて ミスったこと - @dependsを多用した
テスト駆動開発をしばらく取り入れてみて ミスったこと > @dependsを多用した PHPUnitのドキュメントで上から順に学習を進めると、testConstruct()を作成し、そ の返り値を@dependsで次のテストに引き渡すテストケースが完成する https://phpunit.readthedocs.io/ja/latest/writing-tests-for-phpunit.html
テスト駆動開発をしばらく取り入れてみて ミスったこと > @dependsを多用した @dependsで値を引き渡すと不便な点 - テストを単体で実行しようとすると、testConstructが実行されていないのでテ ストが失敗する - 頻繁に@dependsのアノテーションを書き忘れる。タイポする。
引数としての指定を忘れる - @dataProviderの引数と順番を間違える (@dependsの引数は後に来る)
テスト駆動開発をしばらく取り入れてみて ミスったこと > @dependsを多用した setUp()を利用して、TestCaseのプライベート変数としてインスタンスを格納したほう がよい - テストケース毎に初期化されるようになるので、その点注意は必要
テスト駆動開発をしばらく取り入れてみて ミスったこと > 引数としての多次元配列の乱用 - PDOStatement::fetchAll()、TwitterOAuth::get()などの多次元配列をそのまま渡 すコードを書いていた テスト用の値を渡してくれるdataProviderメソッドが非常に長くなったり、簡単に書け る分量でなくなったりする 引数の受け渡しは極力、必要最低限にする方がテストはしやすそう。
-> ただ、全部渡しておいたほうが内部的な変更はしやすそうだなとも感じる
テスト駆動開発をしばらく取り入れてみて やっといてよかったこと - PHPUnitの使い方に慣れる - 写経する。 - 簡単なテストケースを繰り返し作成して、テストケースを作成する作業に慣れる。 PHPUnitは非常に使いやすいツールですが、これにまずは慣れないと、ツールの 使い方に脳のリソースが持ってかれてテスト駆動開発どころではなくなるという状況
に陥りました。
まとめ - テスト駆動開発を行うことによって実感したメリット フィードバックループを回すことによって、一歩一歩確実に開発をすすめることができる 自分の書いたコードが、本当に挙動として自分の思っているとおりに書けているか、セルフ チェックができる - 難しい点 設計
参考 - [動画で解説]和田卓人の“テスト駆動開発”講座 https://gihyo.jp/dev/serial/01/tdd/0001 - 新訳版『テスト駆動開発』 https://t-wada.hatenablog.jp/entry/tddbook