なんかテスト駆動開発記事が人気みたいですので、より詳しい続きを書きますね。
よく閲覧される人気の記事はこちらです。(人気といってもぱっとするわけじゃないんですけどね)
isNumber という簡単な関数に対する簡単なテストコードを書いて同作確認しているコードです。
先の記事を理解した上で、これを応用して複雑な関数に対して複雑なテストコードを書いていくのが上手な成長につながります。
極めてシンプルな形で基本をおさえて、そこから応用していく。それがプログラマというエンジニアにはとっても大事なことです。
テスト駆動開発とは
テストファーストともテスト駆動(ドリブン)とも言われる開発手法です。
もともとは VB や Delphi や C# といった Windows の GUI開発のためのRAD(高速アプリケーション開発)ツールと呼ばれる統合開発環境(IDE)のイベントドリブン開発というものから取られた名前だとは思います。
なんちゃらドリブン開発というのは、ちょっと使うと恥ずかしい感じの流行り言葉の気がしています。モチベーション駆動開発とかドメイン駆動開発とか。そういう中身の実態のなさそうな何やら流行だけで消えていく言葉のような気がしますが、そういうのはさておきまして
テスト駆動開発の方は中身のない言葉ではなく本当に価値のある効果のある開発手法です。私としてはテストファーストやテスト駆動なんていう名前にこだわらずに単に「テストコードを書くことが大事」という開発手法だと思っています。
先の isNumber の記事で
checkEqual(値1, 値2)
で示されているコード部分がテストコードです。
引用しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
checkEqual(true, isNumber(123)); // 数値は数値 checkEqual(true, isNumber(0)); checkEqual(true, isNumber(-1)); checkEqual(true, isNumber(123.4)); // 実数も数値 checkEqual(true, isNumber(123.0)); checkEqual(true, isNumber(10e+3)); // 特殊な記法でも数値 checkEqual(true, isNumber(10e-3)); checkEqual(true, isNumber(020)); // 8進数記法でも数値 checkEqual(true, isNumber(0xFF)); // 16進数記法でも数値 checkEqual(false, isNumber(true)); // Booleanは数値ではない checkEqual(false, isNumber(false)); checkEqual(false, isNumber(null)); // null は数値ではない checkEqual(false, isNumber(undefined)); // undefinde も数値ではない checkEqual(false, isNumber(Infinity)); // Infinity という特殊な数値型の値は Number ではないとしておく checkEqual(false, isNumber(NaN)); // NaN という特殊なという特殊な数値型の値は Number ではないとしておく checkEqual(false, isNumber('')); // 文字は数値ではない checkEqual(false, isNumber('ABC')); checkEqual(false, isNumber('ABC10')); checkEqual(false, isNumber('10ABC')); checkEqual(false, isNumber('0ABC')); checkEqual(false, isNumber('0')); // 文字列は数値に変換可能だとしても文字列なので数値ではない checkEqual(false, isNumber('5')); checkEqual(false, isNumber('-5')); checkEqual(false, isNumber('100')); checkEqual(false, isNumber('-100')); checkEqual(false, isNumber('10e+3')); checkEqual(false, isNumber('10e-3')); checkEqual(false, isNumber('020')); checkEqual(false, isNumber('0xFF')); checkEqual(false, isNumber([])); // 配列は数値ではない checkEqual(false, isNumber([1])); checkEqual(false, isNumber({})); // オブジェクトは数値ではない checkEqual(false, isNumber({ 1: 1})); |
動かすまでもなくcheckEqualの第一引数と第二引数である isNumber の戻り値との値が一致していることが保証されます。
値1と値2に差があれば例外が発生するのでそうならないために値1と値2は常に一致している必要があるのです。
テストファーストとか言われる手法はテストを最初に書くという意味なので、厳密には isNumber の中身の関数実装する前にこの isNumber の関数テストを書くことになります。ですが、自分の場合そこまで厳密にテスト[ファースト]したことはないです。
まあ、関数を書いてその直後にテストを書いて動作確認しておけばよりよいんじゃないでしょうかね。
テストコードを書くというのは古いエンジニアには全くわからないスゴテクです。
簡単なテクニックが故にこの価値を理解している人は少ないです。特に古いタイプのエンジニアと呼ばれる人はこのコードの意味を理解していません。実際にテストコードを日常からすらすら書く人はより少ないです。
ほんとうですか?
証明しておきましょう。
JavaScript オブジェクトの深くネストされているプロパティに安全にアクセスする getProperty - Qiita
Google検索してバグがあるものがみつかります。
こちらも以前に私が書いた記事です。この記事でリンクしました。いくつかのWebページでは不具合のあるコードをテクニックとして公開していたりします。
みなさんテストしていないんですか?
そうです。テストしていないんです。
テストもせずに技術的記事を書くなよ。といいたくなりますが、まあそういっても私だってそういうときはたまにありますので責めてやるなとはいっておきます。
他のWebページからのコピペだとしてもテストをして動作確認してから掲載するようにしたいところです。。
技術的記事を書くような他の人にプログラミング技術を教えるようなハイスキルな人であってもテストコードは書かないということなんです。
テストコードを書くだけであなたが平均以上のプログラマになれるだろう、というのはこういうことです。
ソフトウェアの大事な要素のひとつにプログラムのコード品質があります。
ちゃんと思った通りにプログラムが動くことは当たり前にしても予想もしていなかった値を渡してもプログラムが例外を吐いて(throwして)落ちるといったことがないように配慮してプログラミングしなければなりません。
そのためにあらゆる値を入力してコードの動作確認をしておくということはとても大事なことだったりします。
実際に動かして試してみると簡単に気がつけるのですがリンクしている getPropertyの記事の中で私の書いているコードの中に空文字のような取得できない path を指定して動作を確認しているものがあります。これはコードの品質を確保するためなんです。よくよくコード見てコピペして動かしてみることをおすすめします。
世界一簡単につくれるテストフレームワーク
Qiitaでぼちぼちいいねをもらった記事ですが、以前にこのような内容で記事を書いたことがあります。
こちらの記事は比較用に check 関数にしていますが checkでも checkEqual でも関数名はなんでも好きな命名をするのでよいでしょう。
関数の戻り値が何になるかを想定してコードに記載することで関数それ自体が正しく動いていることを人間がチェックするのではなく、プログラムでチェックするようにさせる。それがテスト駆動開発となります。
こんなライブラリを作っています。
下記のようなライブラリを作っています。実際に仕事で使っていますよ。コピペしたりして。十分にテストを行っているので仕事で使うのも安全です。
世界一簡単なテストフレームワーク 記事の中で紹介したライブラリ
check とか checkResult でテストを実施しています。実装コードとテストコードを並べて配置しています。コード量も多いのでテストコードがどういうものかを見て学ぶ事ができるでしょう。
これの後継ライブラリとして npm で公開しているライブラリを作っています。
こちらは実装コードとテストコードを分離して書いています。
[isNumber]のテストもありますし、[isInterger]のテストもあります。複数の引数を受け付けるために動作確認しているのがわかりますね。
[if_]という関数では間違った書き方をしたら SyntaxError を出力するような機能もつけているのですが checkEqual と isThrownException という関数を使って動作確認しているのもわかるでしょう。丁寧に徹底的にテストするというのはこういうものです。
[guard]という関数でも間違った書き方をすると SyntaxError を出力するのですがテストコード側ではそこまでテストを書いていないものもあります。ちょっとだけテストコードが雑になってしまっています。多分品質的には問題がないと判断して細かくはテストを書かなかったかなと思います。テストをもっと充実させておく必要がありますね。
[if_]や[guard]のテストコードを見てもパット見は理解しがたく段階が高いかもしれません。
テストという A = B が正しいかどうかを判定する単純なものであっても、どんどん極めていけばこういうことになっていくということです。テストコードを極めるには奥の深いものです。
テストコードの概念がわかってきた先に
テストコードの重要性がわかった先に更には assert による契約プログラミングのやり方や、ガード節というプログラミングのやり方を学ぶことができます。
それぞれの書き方を学べばあなたの書くプログラミングコードは非常に品質が高くなっていくでしょう。
契約プログラミングやガード節についてはまた後日の記事で扱おうと思います。
またお知らせしますので、よろしくお願いします。