LoginSignup
387

More than 3 years have passed since last update.

ワイのテスト駆動開発〜偶数ハロー株式会社〜

Last updated at Posted at 2019-07-06

業務中ワイ

ワイ「こないだ見た50分でわかるテスト駆動開発っていう動画おもろかったなぁ」
ワイ「ワイもテスト駆動開発やってみたいわ〜」
ワイ「まずは何か簡単な案件でTDDを導入してみたいわ〜」

そんなとき、クライアントから電話

ジリリリリ〜〜〜〜〜ン!1

20C26BBD-EF6B-4AA7-B9B5-BA5454131FB8.jpeg

ワイ「もしもし〜?」

??「もしもし、株式会社ブラック暗井 暗人(くらい・あんと)ですー」

ワイ「ああ、暗井はんでっか」
ワイ「いつもお世話になってます〜」

暗井「この前ご相談した件なんですけど、正式に発注お願いしますわー」
暗井「株式会社偶数ハローさんのWEBサイトの件ですわー」

ワイ「どんな案件でしたっけ?」

暗井「偶数ハローいう社名にちなんで、」
暗井「トップページにちょっとした仕掛けを設置しよう、いうやつですわ」
暗井「要は───」

  • ユーザーが奇数を入力したら、その数値をそのまま表示する
  • ユーザーが偶数を入力したら、「ハロー」と表示する

暗井「───っていうフォームをトップページに設置するんですわ」

社長「(何やそのクソみたいなフォーム・・・)」

暗井「フォームの見た目とかはもう出来てるんですけど」
暗井「数値かハローを返す関数、これが作れませんで」
暗井「そこの関数だけ作って欲しいんですわ」
暗井「予算は800万ですわ」

ワイ「かしこまりやで」
ワイ「早速そのプログラム作ってみますわ」

ちょうどいい案件が舞い込んできた

ワイ「いや〜」
ワイ「ちょうどTDDの練習におあつらえ向きな」
ワイ「シンプルな案件が舞い込んでくるもんやな〜」

社長「(筆者がそのようにストーリーを作ってるからな・・・)」
社長「(なんや株式会社偶数ハローて。何を願って命名したんや)」

ワイ「ほなTDDしていこか〜」

テストツールを導入する

ワイ「ほなJestいう、有名なテストツールでも導入してみよか」
ワイ「yarn add --dev jestと」
ワイ「おお、簡単やな」
ワイ「そんで、evenHello.jsいうファイルでも作っとこか」

evenHello.js
function evenHello() {

}
module.exports = evenHello;

ワイ「こうやな」
ワイ「evenHelloいう関数を作って、エクスポートするわけや」
ワイ「次はテストを書くファイルを作るで」

test.js
const evenHello = require('./evenHello');

// ここにテストを書いていく

ワイ「こうやな」
ワイ「さっきのevenHello.jsを読み込んで」
ワイ「それに関するテストをここに書いていくんや」

ワイ「ほな、まず関数を書いていこか」
ワイ「とりあえず───」

  • 奇数を渡したら、その数値をそのまま返す

ワイ「───って部分を実装してみよか」

evenHello.js
function evenHello(num) {
    return num;
}
module.exports = evenHello;

ワイ「こうやな」
ワイ「あれ・・・?」

これではいつも通りやん

ワイ「アカンアカン」
ワイ「無意識に実装から始めてもうたわ」
ワイ「ちゃうねん」
ワイ「テスト駆動開発なんやから、テストが先や」
ワイ「実装するより前に、要件を満たしたテストをまず書く
ワイ「そして、そのテストはもちろん通らへん」
ワイ「実装してないからな」
ワイ「そんで、そのあと、テストが通るように実装をしていくねん」

ワイ「先に要件をテスト化してから実装するから」
ワイ「正解に向かってしか進めへんって感じやな」
ワイ「そのためには、ちゃんと要件定義をせなあかんけどな」

ワイ「せやから・・・」
ワイ「一旦evenHello.jsは削除や!

仕切り直してテスト駆動開発

ワイ「まずは要件定義をしてみよか」
ワイ「今回作るべき関数の要件は」

要件チェックリスト:

  • 奇数を渡したら、その数値をそのまま返す
  • 偶数を渡したら、「ハロー」を返す

ワイ「こうやな」
ワイ「さらに細かく、具体的に定義していくで」

要件チェックリスト:

  • 奇数を渡したら、その数値をそのまま返す
    • 1を渡したら、1を返す
    • 3を渡したら、3を返す
  • 偶数を渡したら、「ハロー」を返す
    • 2を渡したら、「ハロー」を返す
    • 4を渡したら、「ハロー」を返す

ワイ「こんな感じやな」
ワイ「ほな、まずは」

  • 1を渡したら、1を返す

ワイ「これのテストを書くで」

test.js
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'

ワイ「せやな」
ワイ「evenHelloCannot findやからfailedや!」
ワイ「evenHello.jsはさっき削除したからな」
ワイ「ほな、テストを通すために、もう一回evenHello.jsを作るで!」

evenHello.js
function evenHello(num) {

}
module.exports = evenHello;

ワイ「こうやな」
ワイ「テストに合わせて、evenHello.jsっていうファイルを作って」
ワイ「テストに合わせて、evenHelloいう関数を書く、と」
ワイ「全てはテスト駆動や!」
ワイ「ほんで、テスト実行、と」

テスト結果:
1を渡したらそのまま1を返す

ワイ「お、一応テストは走ったけど、バツ印やな」
ワイ「ちゃんと実装してへんもんな」

ワイ「ほな、まずこのテストだけ通るように仮実装してみよか」

evenHello.js
function evenHello(num) {
    return num;
}
module.exports = evenHello;

ワイ「こうやな」
ワイ「引数numそのまま返すわけや」
ワイ「ほんで、テスト実行、と」

テスト結果:
1を渡したら、1を返す

ワイ「お、今度は緑のチェックがついたな」
ワイ「ここのテストは通ったいうわけや」

要件チェックリスト:

  • 奇数を渡したら、その数値をそのまま返す
    • 1を渡したら、1を返す
    • 3を渡したら、3を返す
  • 偶数を渡したら、「ハロー」を返す
    • 2を渡したら、「ハロー」を返す
    • 4を渡したら、「ハロー」を返す

ワイ「進捗としては↑こんな感じやな」
ワイ「ほな次は───」

  • 3を渡したら、3を返す

ワイ「───これのテストを書いてみよか」

test.js
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.js
test('2を渡したら、「ハロー」を返す', () => {
    expect(evenHello(2)).toBe('ハロー');
});

ワイ「↑このテストを追加や」
ワイ「ほんで、テスト実行、と」

テスト結果:
1を渡したら、1を返す
3を渡したら、3を返す
2を渡したら、「ハロー」を返す

ワイ「せやな」
ワイ「偶数ハロー機能はまだ実装してへんからな」
ワイ「よっしゃ、今度は実装や」

evenHello.js
function evenHello(num) {
    if (num % 2) {
        return num;
    }
    return 'ハロー';
}
module.exports = evenHello;

ワイ「引数num2で割って」
ワイ「余りがあったなら、num奇数やって事やから」
ワイ「そのままnumを返すんや」
ワイ「逆に、余りがないなら偶数やから」
ワイ「返すのはハローや」

ワイ「これでええやろ」
ワイ「テスト実行、と」

テスト結果:
1を渡したら、1を返す
3を渡したら、3を返す
2を渡したら、「ハロー」を返す

ワイ「うんうん、せやろな」

ワイ「なんかこう、これまでの全要件をテストしてくれるから」
ワイ「継ぎ足し継ぎ足しでコードを書いていっても」

ワイ「さっきまではちゃんと出来てた部分が、知らんうちにダメになってたやん!

ワイ「ってことがないから安心やな」

ワイ「ほな最後は」

  • 4を渡したら、「ハロー」を返す

ワイ「↑この要件のテストでも書いとこか」

test.js
test('4を渡したら、「ハロー」を返す', () => {
    expect(evenHello(4)).toBe('ハロー');
});

ワイ「こうやな」
ワイ「ほんで、テスト実行、と」

テスト結果:
1を渡したら、1を返す
3を渡したら、3を返す
2を渡したら、「ハロー」を返す
4を渡したら、「ハロー」を返す

ワイ「よっしゃ、偶数ハロー関数の完成やな」
ワイ「チェックリストでいうと───」

要件チェックリスト:

  • 奇数を渡したら、その数値をそのまま返す
    • 1を渡したら、1を返す
    • 3を渡したら、3を返す
  • 偶数を渡したら、「ハロー」を返す
    • 2を渡したら、「ハロー」を返す
    • 4を渡したら、「ハロー」を返す

ワイ「───こんな感じや」
ワイ「オールOKや」

せっかくだから、チェックリストに近いテストを

ワイ「確かJestの機能で」
ワイ「テストをグループ化できんねん」

test.js
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を渡したら、「ワールド」を返す

ワイ「こんな感じやな」
ワイ「せやから・・・」

test.js
describe('負の値を渡したら、偶数奇数に関わらず「ワールド」を返す', () => {
    test('-1を渡したら、「ワールド」を返す', () => {
        expect(evenHello(-1)).toBe('ワールド');
    });
    test('-2を渡したら、「ワールド」を返す', () => {
        expect(evenHello(-2)).toBe('ワールド');
    });
});

ワイ「↑これをテストに追加や!」
ワイ「そんでテスト実行!」

テスト結果:
負の値を渡したら、偶数奇数に関わらず「ワールド」を返す
  -1を渡したら、「ワールド」を返す
  -2を渡したら、「ワールド」を返す
奇数を渡したら、その数値をそのまま返す
  1を渡したら、1を返す
  3を渡したら、3を返す
偶数を渡したら、「ハロー」を返す
  2を渡したら、「ハロー」を返す
  4を渡したら、「ハロー」を返す

ワイ「せやな」
ワイ「追加したテストだけバツ。想定通りや」
ワイ「ほんで今度は実装や!」

evenHello.js
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を見れば」
ワイ「おー、こんな要件を満たす必要があるんやなー、って分かるやん?」
ワイ「そういう情報がコードとして残るのが素晴らしいな」

ワイ「よっしゃ、今度はもうちょい複雑な案件でもテスト駆動開発してみよか!」

〜おしまい〜

おすすめ動画

ワイの記事はだいぶ端折ってる感じやから、興味がある人は↓この動画を見るとええで!

50分でわかるテスト駆動開発


  1. 電話の音やで。 

  2. 前は機能していた部分がバグってしまうことや。 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
387