業務中ワイ
ワイ「こないだ見た**50分でわかるテスト駆動開発っていう動画おもろかったなぁ」
ワイ「ワイもテスト駆動開発やってみたいわ〜」
ワイ「まずは何か簡単な案件でTDD**を導入してみたいわ〜」
そんなとき、クライアントから電話
ジリリリリ〜〜〜〜〜ン!1
ワイ「もしもし〜?」
??「もしもし、株式会社ブラックの**暗井 暗人(くらい・あんと)**ですー」
ワイ「ああ、暗井はんでっか」
ワイ「いつもお世話になってます〜」
暗井「この前ご相談した件なんですけど、正式に発注お願いしますわー」
暗井「株式会社偶数ハローさんのWEBサイトの件ですわー」
ワイ「どんな案件でしたっけ?」
暗井「偶数ハローいう社名にちなんで、」
暗井「トップページにちょっとした仕掛けを設置しよう、いうやつですわ」
暗井「要は───」
- ユーザーが奇数を入力したら、その数値をそのまま表示する
- ユーザーが偶数を入力したら、「ハロー」と表示する
暗井「───っていうフォームをトップページに設置するんですわ」
社長「(何やそのクソみたいなフォーム・・・)」
暗井「フォームの見た目とかはもう出来てるんですけど」
暗井「数値かハローを返す関数、これが作れませんで」
暗井「そこの関数だけ作って欲しいんですわ」
暗井「予算は800万ですわ」
ワイ「かしこまりやで」
ワイ「早速そのプログラム作ってみますわ」
ちょうどいい案件が舞い込んできた
ワイ「いや〜」
ワイ「ちょうどTDDの練習におあつらえ向きな」
ワイ「シンプルな案件が舞い込んでくるもんやな〜」
社長「(筆者がそのようにストーリーを作ってるからな・・・)」
社長「(なんや株式会社偶数ハローて。何を願って命名したんや)」
ワイ「ほなTDDしていこか〜」
テストツールを導入する
ワイ「ほな**Jest**いう、有名なテストツールでも導入してみよか」
ワイ「yarn add --dev jest
と」
ワイ「おお、簡単やな」
ワイ「そんで、evenHello.js
いうファイルでも作っとこか」
function evenHello() {
}
module.exports = evenHello;
ワイ「こうやな」
ワイ「**evenHello
**いう関数を作って、エクスポートするわけや」
ワイ「次はテストを書くファイルを作るで」
const evenHello = require('./evenHello');
// ここにテストを書いていく
ワイ「こうやな」
ワイ「さっきのevenHello.js
を読み込んで」
ワイ「それに関するテストをここに書いていくんや」
ワイ「ほな、まず関数を書いていこか」
ワイ「とりあえず───」
- 奇数を渡したら、その数値をそのまま返す
ワイ「───って部分を実装してみよか」
function evenHello(num) {
return num;
}
module.exports = evenHello;
ワイ「こうやな」
ワイ「あれ・・・?」
これではいつも通りやん
ワイ「アカンアカン」
ワイ「無意識に実装から始めてもうたわ」
ワイ「ちゃうねん」
ワイ「テスト駆動開発なんやから、テストが先や」
ワイ「実装するより前に、要件を満たしたテストをまず書く」
ワイ「そして、そのテストはもちろん通らへん」
ワイ「実装してないからな」
ワイ「そんで、そのあと、テストが通るように実装をしていくねん」
ワイ「先に要件をテスト化してから実装するから」
ワイ「正解に向かってしか進めへんって感じやな」
ワイ「そのためには、ちゃんと要件定義をせなあかんけどな」
ワイ「せやから・・・」
ワイ「一旦evenHello.js
は削除や!」
仕切り直してテスト駆動開発
ワイ「まずは要件定義をしてみよか」
ワイ「今回作るべき関数の要件は」
要件チェックリスト:
- 奇数を渡したら、その数値をそのまま返す
- 偶数を渡したら、「ハロー」を返す
ワイ「こうやな」
ワイ「さらに細かく、具体的に定義していくで」
要件チェックリスト:
- 奇数を渡したら、その数値をそのまま返す
- [ ]1
を渡したら、1
を返す
- [ ]3
を渡したら、3
を返す
-
偶数を渡したら、「ハロー」を返す
-
2
を渡したら、「ハロー」を返す -
4
を渡したら、「ハロー」を返す
-
ワイ「こんな感じやな」
ワイ「ほな、まずは」
1
を渡したら、1
を返す
ワイ「これのテストを書くで」
const evenHello = require('./evenHello');
test('1を渡したら、1を返す', () => {
expect(evenHello(1)).toBe(1);
});
ワイ「こうやな」
ワイ「ほんでテスト実行や」
ワイ「yarn test
と」
Test suite failed to run
Cannot find module './evenHello' from 'test.js'
ワイ「せやな」
ワイ「evenHello
がCannot find
やからfailed
や!」
ワイ「evenHello.js
はさっき削除したからな」
ワイ「ほな、テストを通すために、もう一回evenHello.js
を作るで!」
function evenHello(num) {
}
module.exports = evenHello;
ワイ「こうやな」
ワイ「テストに合わせて、evenHello.js
っていうファイルを作って」
ワイ「テストに合わせて、evenHello
いう関数を書く、と」
ワイ「全てはテスト駆動や!」
ワイ「ほんで、テスト実行、と」
テスト結果:
✕ 1を渡したらそのまま1を返す
ワイ「お、一応テストは走ったけど、バツ印やな」
ワイ「ちゃんと実装してへんもんな」
ワイ「ほな、まずこのテストだけ通るように仮実装してみよか」
function evenHello(num) {
return num;
}
module.exports = evenHello;
ワイ「こうやな」
ワイ「引数num
をそのまま返すわけや」
ワイ「ほんで、テスト実行、と」
テスト結果:
✓ 1を渡したら、1を返す
ワイ「お、今度は緑のチェックがついたな」
ワイ「ここのテストは通ったいうわけや」
要件チェックリスト:
- 奇数を渡したら、その数値をそのまま返す
- [x]1
を渡したら、1
を返す
- [ ]3
を渡したら、3
を返す
-
偶数を渡したら、「ハロー」を返す
-
2
を渡したら、「ハロー」を返す -
4
を渡したら、「ハロー」を返す
-
ワイ「進捗としては↑こんな感じやな」
ワイ「ほな次は───」
3
を渡したら、3
を返す
ワイ「───これのテストを書いてみよか」
const evenHello = require('./evenHello');
test('1を渡したら、1を返す', () => {
expect(evenHello(1)).toBe(1);
});
test('3を渡したら、3を返す', () => {
expect(evenHello(3)).toBe(3);
});
ワイ「これは、evenHello
関数は特にいじらなくてもテスト通りそうやな」
ワイ「ほなテスト実行、と」
テスト結果:
✓ 1を渡したら、1を返す
✓ 3を渡したら、3を返す
ワイ「よっしゃ、通ったな」
ワイ「ほな次は・・・テストをまた書くんやな」
ワイ「テストが先やからな」
test('2を渡したら、「ハロー」を返す', () => {
expect(evenHello(2)).toBe('ハロー');
});
ワイ「↑このテストを追加や」
ワイ「ほんで、テスト実行、と」
テスト結果:
✓ 1を渡したら、1を返す
✓ 3を渡したら、3を返す
✕ 2を渡したら、「ハロー」を返す
ワイ「せやな」
ワイ「偶数ハロー機能はまだ実装してへんからな」
ワイ「よっしゃ、今度は実装や」
function evenHello(num) {
if (num % 2) {
return num;
}
return 'ハロー';
}
module.exports = evenHello;
ワイ「引数num
を2
で割って」
ワイ「余りがあったなら、num
は奇数やって事やから」
ワイ「そのままnum
を返すんや」
ワイ「逆に、余りがないなら偶数やから」
ワイ「返すのはハロー
や」
ワイ「これでええやろ」
ワイ「テスト実行、と」
テスト結果:
✓ 1を渡したら、1を返す
✓ 3を渡したら、3を返す
✓ 2を渡したら、「ハロー」を返す
ワイ「うんうん、せやろな」
ワイ「なんかこう、これまでの全要件をテストしてくれるから」
ワイ「継ぎ足し継ぎ足しでコードを書いていっても」
ワイ「さっきまではちゃんと出来てた部分が、知らんうちにダメになってたやん!」
ワイ「ってことがないから安心やな」
ワイ「ほな最後は」
4
を渡したら、「ハロー」を返す
ワイ「↑この要件のテストでも書いとこか」
test('4を渡したら、「ハロー」を返す', () => {
expect(evenHello(4)).toBe('ハロー');
});
ワイ「こうやな」
ワイ「ほんで、テスト実行、と」
テスト結果:
✓ 1を渡したら、1を返す
✓ 3を渡したら、3を返す
✓ 2を渡したら、「ハロー」を返す
✓ 4を渡したら、「ハロー」を返す
ワイ「よっしゃ、偶数ハロー関数の完成やな」
ワイ「チェックリストでいうと───」
要件チェックリスト:
- 奇数を渡したら、その数値をそのまま返す
- [x]1
を渡したら、1
を返す
- [x]3
を渡したら、3
を返す
-
偶数を渡したら、「ハロー」を返す
-
2
を渡したら、「ハロー」を返す -
4
を渡したら、「ハロー」を返す
-
ワイ「───こんな感じや」
ワイ「オールOKや」
せっかくだから、チェックリストに近いテストを
ワイ「確かJestの機能で」
ワイ「テストをグループ化できんねん」
describe('奇数を渡したら、その数値をそのまま返す', () => {
test('1を渡したら、1を返す', () => {
expect(evenHello(1)).toBe(1);
});
test('3を渡したら、3を返す', () => {
expect(evenHello(3)).toBe(3);
});
});
describe('偶数を渡したら、「ハロー」を返す', () => {
test('2を渡したら、「ハロー」を返す', () => {
expect(evenHello(2)).toBe('ハロー');
});
test('4を渡したら、「ハロー」を返す', () => {
expect(evenHello(4)).toBe('ハロー');
});
});
ワイ「こないな感じや」
ワイ「チェックリスト同様に入れ子にしてやるんや」
ワイ「そしたら、今後この案件に関わる子らにとっても分かりやすいやろ」
ワイ「ほんで、テスト実行してみよか」
テスト結果:
奇数を渡したら、その数値をそのまま返す
✓ 1を渡したら、1を返す
✓ 3を渡したら、3を返す
偶数を渡したら、「ハロー」を返す
✓ 2を渡したら、「ハロー」を返す
✓ 4を渡したら、「ハロー」を返す
ワイ「うんうん」
ワイ「ちゃんとテスト結果もグループ分けされてて直感的やん」
ワイ「はぁ〜」
ワイ「デグレ2してないことがテストによって保証されてるから」
ワイ「安心して進んでいける感じがしたな」
ワイ「ほな、クライアントはんに連絡してみよか」
暗井暗人はんに報告
ワイ「もしもし〜、暗井はん?さっきの関数作りましたで〜」
ワイ「いつものリポジトリに上げといたんで、確認してくださいやで〜」
暗井「はいよー」
暗井「うんうん、動かしてみたけどええ感じやね」
ワイ「よかったですわ〜」
暗井「・・・あっ!!!」
ワイ「な、なんでっか・・・」
暗井「株式会社偶数ハローさん、先週社名変更したんでしたわ」
暗井「その名も、株式会社偶数ハロー・マイナスワールド」
暗井「それに伴って関数の仕様にも───」
- 負の値を渡したら、「ワールド」を返す
暗井「───これが追加になるんでしたわ」
暗井「さっき言い忘れてましたわ」
ワイ「はぁ・・・・」
暗井「すんまへんけど、その修正お願いしますわ・・・」
暗井「ちなみにこの修正も、さっきの800万の中で収まりまっか・・・?」
ワイ「何とか800万で収めますわ・・・」
修正開始
ワイ「はぁ、一番やる気なくなるやつや・・・」
ワイ「でもまぁ、テスト駆動開発の練習にはちょうどええかもな」
ワイ「さっそく、要件定義&テストから始めるで!」
ワイ「追加の要件は・・・」
- 負の値を渡したら、偶数奇数に関わらず「ワールド」を返す
- [ ]-1
を渡したら、「ワールド」を返す
- [ ]-2
を渡したら、「ワールド」を返す
ワイ「こんな感じやな」
ワイ「せやから・・・」
describe('負の値を渡したら、偶数奇数に関わらず「ワールド」を返す', () => {
test('-1を渡したら、「ワールド」を返す', () => {
expect(evenHello(-1)).toBe('ワールド');
});
test('-2を渡したら、「ワールド」を返す', () => {
expect(evenHello(-2)).toBe('ワールド');
});
});
ワイ「↑これをテストに追加や!」
ワイ「そんでテスト実行!」
テスト結果:
負の値を渡したら、偶数奇数に関わらず「ワールド」を返す
✕ -1を渡したら、「ワールド」を返す
✕ -2を渡したら、「ワールド」を返す
奇数を渡したら、その数値をそのまま返す
✓ 1を渡したら、1を返す
✓ 3を渡したら、3を返す
偶数を渡したら、「ハロー」を返す
✓ 2を渡したら、「ハロー」を返す
✓ 4を渡したら、「ハロー」を返す
ワイ「せやな」
ワイ「追加したテストだけバツ。想定通りや」
ワイ「ほんで今度は実装や!」
function evenHello(num) {
if (num < 0) {
return 'ワールド';
}
if (num % 2) {
return num;
}
return 'ハロー';
}
module.exports = evenHello;
ワイ「こうやな!」
ワイ「ほんでテスト実行や!」
テスト結果:
負の値を渡したら、偶数奇数に関わらず「ワールド」を返す
✓ -1を渡したら、「ワールド」を返す
✓ -2を渡したら、「ワールド」を返す
奇数を渡したら、その数値をそのまま返す
✓ 1を渡したら、1を返す
✓ 3を渡したら、3を返す
偶数を渡したら、「ハロー」を返す
✓ 2を渡したら、「ハロー」を返す
✓ 4を渡したら、「ハロー」を返す
ワイ「うんうん」
ワイ「追加の要件も満たしたし」
ワイ「元々の機能もおかしくなってへん」
ワイ「それが可視化されてるからものすごい安心感や!」
ワイ「こんなに安心して追加開発が出来るのは初めてやで・・・」
ワイ「いつも、コード修正するときに」
ワイ「言われた部分は修正できたけど、他んとこおかしくなってへんかな・・・って」
ワイ「心配で夜も6時間しか眠れへんかったんや」
社長「(まあまあ寝てるやん・・・)」
社長「(あと、そんな心配なもん納品すな・・・)」
まとめ
ワイ「なんか、追加開発時の安心感がすごいわ」
ワイ「デグレしてへんことをテストが保証してくれてんねんもん」
ワイ「あと、テストコード自体がドキュメント代わりになるな」
ワイ「途中から参加したメンバーも、test.js
を見れば」
ワイ「おー、こんな要件を満たす必要があるんやなー、って分かるやん?」
ワイ「そういう情報がコードとして残るのが素晴らしいな」
ワイ「よっしゃ、今度はもうちょい複雑な案件でもテスト駆動開発してみよか!」
〜おしまい〜
おすすめ動画
ワイの記事はだいぶ端折ってる感じやから、興味がある人は↓この動画を見るとええで!