🔖

テストないとか、それケニーの前で言えんの?①

2023/05/29に公開

はじめに

煽りから入るパターンで書き始めます。ケニーです。
なんとなくテストの重要性は頭で分かってはいるものの、後回しにしてしまう事ってないですか?
最近の私は業務でテストに関わる事が多く、学びも非常に多いので、複数回に分けてアウトプットしてみようと思います。
第一回目はテスト駆動開発についてです。

テスト駆動開発とは

【テストケースから書いて、実装していく手法】 で、
Test-Driven Development:TDDと略して記載するケースが多いです。

例)テストを書かずに、実装をした場合

  • 数か月後にコードの修正をしたら、すべて正しく動作している事を保証できますか?
  • 他にデグレは無いと断言できますか?
  • 修正や機能追加する度に、すべての動作を手動で確認しないといけませんか?

メリット

後に上記のような課題に直面する前に、手を打っておきましょう。というのが今回の手法。
テストコードがある事によって、

  • デバッグ工数や保守コスト削減
  • 関数が正しく動作する事を保証できる(安心感につながる!)
  • いつでもテストの実行ができる
  • テストコードから仕様が推測しやすくなる。

デメリット

デメリットというかハードルですが。

  • 事前にテストコードの知識が必要(例:Jestの書き方)
  • 開発L/Tで、テストをかく工数がかかる
     ⇒テストを書く時間がないのではなく、テストを書かないから時間がなくなる
     ⇒テストを書くことで、実工数は15%から35%増加するといわれてる
     その代わりに、システムの欠陥密度を4割から9割低下させ、
     デバッグや手戻り工数の減少、トータルの工数を削減するなどの効果を得ることができる。

TDDの手順について

基本的には、以下手順で実行します。
なお、ペアプロ等でやると、よりよい品質を作りこむことができるので、効果が高まるようです。

1)テストコードを書く

想定されるケースを考慮しつつ、テストコードを書きます。

2)テストを実行する(失敗する)

テストを実行するのですが、この時点では実装はまだしていないので、
失敗(レッド)することを確認します。

3)テストケースにパスするように実装

実際に実装します。

4)テストを実行する(成功させる)

すべてのテスト結果が、成功(グリーン)するまで、
コードを修正していきます。

5)成功させた状態を保持しつつリファクタリング

全体的なパフォーマンスを向上させる為、
・コードの保守性
・可読性
を意識しつつ、リファクタリングしていきます。

一度やってみましょう

【前提】
・Node.js(v16.14.2)
・Jest (v24.9.0)

ゴール:足し算と引き算の関数を1個ずつ実装したい

1)テストコードを書く

今回はJestでテストコードを書いてみます。(Jestについて

calc.spec.ts
'use strict';
import { calc } from '../src/calc';

describe('足し算関数のテストをする', () => {
  it('1 + 1 = 2である', () => {
    expect(calc.add(1, 1)).toBe(2);
  });

  it('1 + 2 = 3である', () => {
    expect(calc.add(1, 2)).toBe(3);
  });

});

describe('マイナス関数のテストをする', () => {
  it('1 - 1 = 0である', () => {
    expect(calc.minus(1, 1)).toBe(0);
  });
});

describe:大きな塊の名前
it:単体テスト 
expect:文字通り、テストに期待すること
toBe:テスト結果の期待値
と最初は覚えていればよいのかな。

2)テストを実行する(失敗する)

1)のテストコードしか書いていない状態でテストを実行してみます。
npm run testを実行します

package.json
"scripts": {
    "test": "jest",
    "test:coverage": "jest --coverage",
  },

結果は以下のとおり。

そりゃFAILしますよね。
まだ実装していないので。

3)テストケースにパスするように実装

ここでようやく実装段階にきました。
何を実装しないといけないのかは、テストコードを書いたので、明確になっています。

・足し算をする関数
・引き算をする関数

calc.ts
export class calc {
  public static add(x: number, y: number): number {
    return x + y;
  }

  public static minus(x: number, y: number): number {
    return x + y;
  }
}

はい、実装しました。

4)テストを実行する(成功させる)

結果は、

おやおや?
FAILとなりました。
ということは、関数のどこかが間違っているようです。
FAILになった箇所は、マイナス関数のようですね。

コードを見返すと、
引き算をする関数で、
return が x + y となっており、足し算をする実装になっていたんですね。

calc.ts
export class calc {
  public static add(x: number, y: number): number {
    return x + y;
  }

  public static minus(x: number, y: number): number {
    return x - y; // x + y になっていたので、 x - y に修正
  }
}

はい、引き算に修正しました。

結果は、

全て合格!PASSしました。

その他

なお、Jestには以下のような使い方(確認の仕方)もあるので、
復習も兼ねて実行してみます。

npm run test:coverage

上記キャプチャの下半分に注目です!

実施したテストは実装全体のどのくらいをカバーしているかを表示できます。

今回は、calc.tsに足し算と引き算の関数2つあり、2つともテストした為、
100%カバーされている結果となっています。

終わりに

このように最初時間はかかるものの、
テストコードを最初にしっかり書いておくことで、
実装のチェックをいつでも素早く確実に行う事ができます。
最終的には品質を担保できるので、非常にメリットはあるのではないのでしょうか。

個人的には、想像以上に効果がでそうだなと感じたのは、
テストコードを書いたことで、事前に仕様が明確になっているため、迷う事なく、実装開始できる点でした。
また、品質を常に担保できる安心感は、最初に多少時間かかってでも取り組む価値はあるのではないかと感じています。

コラボスタイル Developers

Discussion