「コード」カテゴリーアーカイブ

テスト駆動開発で使う テストフレームワークの仕組み


Test-Driven Development By Example (Kent Beck)
Kent Beck の Test-Driven Development By Example を Amazon で買う

参考書籍は Test-Driven Development By Example、 日本語版は テスト駆動開発入門です。

Money Example の次は jUnit のようなテストツールを作ってみるお話です。 30以上の言語で使われている xUnit は、 今や巨大な資産ですが、 そういったものは自分で作れるものなんでしょうか。

テストフレームワークの仕組み

読んでみると、実際にやっていることは難しくなさそうです。 肝となるのは 一連のテストの結果 (実行・成功・失敗したテスト数) をどうやって保持するかではないかと思いました。 独自にテストフレームワークを作るときに役に立ちそうです。

社内でも独自にテストフレームワークを作っている人がいます、 かなりシンプルにできるそうです。 その人によると、 PHPUnit などのテストフレームワークは いろんなファンクションや機能をつけているから複雑になっているそうです。 そうじゃないと使ってもらえないからだとも言っていましたが。

JUnit や PHPUnit は TestCase だったかのクラスを継承してテストコードを書いていくので 今回説明されていたやり方と同じですね。 でも rspec だと describe で書き始めたりするし クラス の 定義 で始めるわけでもないので 本書とは別のアプローチかもしれません。

Test-Driven Development By Example (Kent Beck)
Kent Beck の Test-Driven Development By Example を Amazon で買う

テスト駆動開発でテストを書かない場合がある


Test-Driven Development By Example (Kent Beck)
Kent Beck の Test-Driven Development By Example を Amazon で買う

参考書籍は Test-Driven Development By Example、 日本語版は テスト駆動開発入門です。

機能追加の方針

Franc-ly Speaking のところ(29ページ)からです。 Franc を導入して複数通貨に対応させるんですね。 39ページに至るまでの流れを見ていくと次のようになります。

  1. Dollar を複製する。 テストコードも複製する。
  2. 親クラス Money と 継承クラス Dollar, Franc に分ける。
  3. Factory Method PatternMoney クラス に共通のものを集約する。

Dollar から Money へ、 子クラスから親クラスへメソッドを移す手順には要注意ですね。

書かないテスト

途中、 toString() というメソッドを、テストコードを書かずに追加しています。 この判断基準は重要なのでまとめておきます。

次の条件を満たす場合は、テストコードを書かなくてもOKとします。

  • 画面上で(テストの)結果を見ようとするメソッドを追加しようとしている。
  • 追加するコードはデバッグのためのコードで、仮に正常に動かなくなったとしてもダメージが小さい。
  • テストが Red で終了している。 (Red のときは新しいテストコードを書きたくない。)

普遍的にまとめた記述が今のところ出てきていないので toString() 追加の際の説明をまとめたものになっています。

toString() 程度 だったらテストを書いてもよさそうですが、 もっと複雑なものに成った場合には テストを書かずに進めた方がいいんでしょうか。 これは実践あるのみですね。

その後 Money Example の演算の実装と、 全体を俯瞰した傾向、 テストの品質について重要な記述がありますが、 ひとつひとつ紹介すると長くなるので割愛します。 でもひとつだけ気になる記述があるので紹介します。

Easy to read for programmers – Unsynchronized documentation is scarce. The tests will be more valuable if they are readable, giving an interesting second perspective on the messages hidden in the source code.

テストを書けば仕様も理解しやすくなるかもしれないと思いました。

Test-Driven Development By Example (Kent Beck)
Kent Beck の Test-Driven Development By Example を Amazon で買う

テスト駆動開発の始め方


Test-Driven Development By Example (Kent Beck)
Kent Beck の Test-Driven Development By Example を Amazon で買う

約70ページにもわたって MONEY EXAMPLE という事例が書かれています。 あまりに多いので一気に読むのはちょっと辛いですが、テストドリブンを夢見てがんばって読んでいます。 今回は テストドリブンの序盤となる部分をyんでみました。

実装の前にテストをすべて書くのは無理

MONEY EXAMPLE のところを見ると、 Kent Beck ですら最初にすべてのコードを書かないということがわかります。 MONEY EXAMPLE で取り上げられている単純な金額の掛け算でも、コードが徐々に完成していくのにつれてテストコードも変わっていきます。 よく、最初にテストコードを書くなんて無理だからテスト駆動開発は無理だという人がいますが、テスト駆動とはそういうことではないんですね。

MONEY EXAMPLE では、 存在しないクラスを使ってテストを書き始めます。 Red どころか コンパイルすらできないテストを書くところから始めています。 必要なクラスを考えてクラスのテストから書き始めるのかと思いましたがそうではありませんでした。 最後の理想形をテストに書いていました。

テストドリブンの手順

ToDo の整理をしてからテストを始めます。 この ToDo List はコードが完成するまでついて回ります。 載っている事例では、 存在しない Dollar クラス を使ってテストを書くところから始めています。 そして テストを通してできるクラスのメソッドは(最初は)あくまでスタブです。

MONEY EXAMPLE は次のようにして進めていました。

  1. テストを書く。存在しないクラスも使ってよい。
  2. テストをコンパイルできるようにする。
  3. テストを Green にする。
  4. リファクタを行い、重複を取り除く。

テストを Green にする方法

  • 固定値を返すなどして、テストを通るようにする。 (実装コードとしては完成ではない)
  • 明らかにわかるコードを実装する。
  • 複数の側面からのテストを書く。 (Triangulation)

Triangulation というのは、 テストケースを追加することで固定値での返り値を返せないようにするように、複数のパターンのテストを書くことで実装コードを規定していく方法です。 たとえば 1 から n までの総和を求める関数があったとして、 Sum(1) == 1 だけだったら Sum の返り値は 固定の 1 でもいいですが、 Sum(5) == 15 だと実装せざるを得なくなります。 Kent Beck は本当にどのようにリファクタすればよいか迷うようなときに Triangulation を使うそうです。 どうすればいいのかわかっている場合は Triangulation のためのテストコードは書かれません。

MONEY EXAMPLE の中で出てきた Value Object というのも重要だと思うのでまとめておきます。

Value Object

一種のインスタンス変数で、コンストラクタで設定された値はそれ以降変わることがありません。 これにより、ポインタを通じた意図しない値の書き換えの発生を防ぎます。 別の言葉で書くなら 副作用対策です。

Value Object の持つメソッドの戻り値は必ず新しいオブジェクトにします。 下に例を書きました。 Numadd メソッドの戻り値は新しいインスタンスになっています。 これにより add の前後で Num のインスタンスは変更することがありません。 また、 primitive value ではないので、 別途 equal メソッドを実装しておかないとテストコードを書くときに困ることがあります。

Test-Driven Development By Example の JAVA のコードをまねて作ったのですが、 言語が ruby なので実践的には get_amount を書かずに attr_reader :amount と書きますね。

Test-Driven Development By Example (Kent Beck)
Kent Beck の Test-Driven Development By Example を Amazon で買う

FuelPHP で使用可能なデータベース


FuelPHP でどのデータベースが使えるのか、コードを覗いてみました。

環境

  • FuelPHP 1.8 (GitHub)

使用可能なデータベース

FuelPHP では 次の DBMS を扱うことができます。 扱うことができるといってもドライバとして参照するというだけで、 完璧に使えるわけではありません。

  • Cubrid
  • FreeTDS
  • Microsoft SQL Server
  • Sybase
  • Firebird
  • IBM DB2
  • IBM Informix Dynamic Server
  • MySQL
  • Oracle Call Interface
  • ODBC v3 (IBM DB2, unixODBC and win32 ODBC)
  • PostgreSQL
  • SQLite 3, 2
  • SQL Azure
  • 4D

調べ方

まずはマニュアルを読みます。 Database Introduction のところに Can be mysql, mysqli or pdo と あります。 つまり、 mysql の専用ドライバか、PDOドライバが使えるんですね。

続いて PDO について PHP のドキュメント を確認します。 すると、上述の DBMS すべてについて PDO が使えることがわかります。 もちろん PDOドライバがマシンに入っていればの話ですし、PDOとDBMSのバージョンには気をつけないといけませんね。

本当に PDO で繋がるのか? と思ったら、 FuelPHP のコアファイルの中の接続部分を担っているコードを見ましょう。 mysql, mysqli, PDO の3通りの connection.php があり、 PDO であれば PDO ドライバを使って DB に接続するようになっていることがわかります。

sqlite を使う場合の注意点

sqlite を使う場合にはいくつか気をつけることがあります。 (FuelPHP version 1.7.2)

Primary Key に注意

Primary Key を他のデータベースと同じように設定しようとすると migration が動きません。 sqlite3 では、 Primary Key に名前をつけることができません。 例えば MySQL なら Primary Key id (id) となるところが、 sqlite3 だと Primary Key (id) というようになります。

直接SQLを作って実行するか、 FuelPHP の該当箇所を書き換えるかして凌ぐしかないです。 Primary Key を使わないという選択肢もありますが、おすすめはしません。

また通常は migration file を php oil を使って作成すると 自動で UNSIGNEDid が作成されて AUTO_INCREMENT が設定されますが、 sqlite ではこれが動きません。 sqlite では AUTO_INCREMENT ではなく AUTOINCREMENT になります。 いまのところ自動生成のスクリプトでは対応していませんので FuelPHP の core のコードを変更する必要があります。 そして自動生成される UNSIGNED は 削除しましょう。 sqlite では UNSIGNED BIT INT というのが使えますが、 使ったところで INTEGER と解釈されます。

charset を空にする

sqlite3 では charset の指定方法が他のデータベースとは異なります。 PRAGMA statement を使います。 (確か sqlite3 では標準文字コードが utf-8 だったと思います。)

データベースの設定(config/db.php)で文字コードの指定をしないと php oil migrate を実行する際に CREATE TABLE の構文で 末尾に DEFAULT CHARACTER SET utf8 のついたSQLが発行されてしまい、エラーになります。 これを防ぐためには charset => '' と、 charset に 空文字列 を指定します。 すると、 DEFAULT から始まる文字コード指定部分がなくなり、 sqlite3 でも php oil migrate が動くようになります。

db.php は次のように書きます。

このようにすると、 環境ごとに別の DB が作成されます。 table_prefixphp oil r migration の時には不要ですが、 php oil r migration:down の時に必要になることがあります。

PostgreSQL を使う場合の注意点

migration ファイル を作っても動きません。 これは Syntax が MySQL とだいぶ違うからです。 FuelPHP に PostgreSQL を使うためのプラグインがあるのでそちらを使うという方法もあります。

charset を空にする

charset に値が入っていると 接続情報(dns)に charset が含まれるようになるのですが、 PostgreSQL はこれを受け付けません。 そこで charset = '' と設定しておきましょう。 この点は SQLITE と同じです。

constraint を削除する

php oil g migration create_xxx のようにして テーブル作成のマイグレーションファイルを作成すると、 id カラム が自動的に作成され、 'constraint' => 11 という設定がされます。 この状態だと テーブル作成の SQL で id カラムは INT(11) に指定されます。 しかし、 PostgreSQL は INT(11) を受け付けません。 INT なら SQL が通ります。

そこで 'constraint' => 11 の設定を削除します。


VBA: ファイルの一覧を再帰的に出力する


よく使うコードのメモです。

Excel などで、あるフォルダ以下のファイル一覧を出力したいときに使うコードです。 言語は VBA です。

VBA は オブジェクト指向的 に書くこともできるのですが、 急遽必要だったので、「とりあえず動けばいい!」というスタイルで作りました。

プログラムの流れ

  1. シート上のボタンをクリックします。
  2. フォルダ選択ダイアログを表示します。
  3. 選択されたフォルダ以下のファイルを再帰的にリストにします。
  4. リストになったファイル一覧を、フォーム(またはシート)に出力します。

やること

  • モジュールを作成して、次の関数を作成します。
    • ファイル一覧を取得する
    • フォルダ選択ダイアログを処理する
  • 結果を出力するダイアログを作る。
  • シートにボタンを作ってクリックしたときの処理を追加する。
続きを読む VBA: ファイルの一覧を再帰的に出力する