Yabu.log

ITなどの雑記

「テスト駆動開発」読書会 Vol.6 に参加

テスト駆動開発

テスト駆動開発

23章から28章を読みました。

xunitは自作するようなものなのか?

24章にxUnitは作って当然、といった態度の記述がある。

私が新しいプログラミング言語に触るときには、まずxUnitを実装してみる。8個から10個のテストが動作するようになるころには、日々のプログラミングに必要な機能は登場していることだろう。(テスト駆動開発24章より)

過去のプロジェクトで開発に試用版(Express Edition)を使っていたため、MSTestが使えず、また客先なのでライブラリの導入も難しいという実体験がありましたが、こういう時に簡単なテストツールをサクッと作るようなものなのか、という私自身の実体験から話題に挙げてみました。*1

普通はだいたいどの環境にも標準のものが用意されているので、必要になって作ったりしない。これは趣味の領域。*2ということだそうです。

Assertionエラーと実行時エラーの扱いを分けることについて

xUnitを使い始めると、アサーションの失敗と、テスト実行中に起こる他のエラーとの大きな違いに気がつく。アサーションの失敗は、デバッグにずっと多くの時間がかかる傾向にある。このため、xUnit実装の多くは失敗(アサーションの失敗)とエラーとを区別する。UIも2つを分けて表示する。特にエラーを上のほうに表示することが多い。

テスト駆動開発24章より引用

  • Error:実行時のエラー(ぬるぽとかの実行時例外等)
  • fail:Assertionの失敗(メソッド戻り値が期待していたものと違う)

は分ける必要があるのか?

  • 実行時のエラーはプログラムが壊れている、Assertionのエラーはロジックが壊れているということでは?
  • 一部のビルドツール(Ant)ではテスト中の実行時エラーをビルド失敗とみなすのでこの違いは重要。

悩み:Todoが爆発してしまう

とりあえずTDDを始めたが、うまくいかずタスクに親(前提として行う必要がある作業)子供と孫(そのタスクのサブタスク) が増殖してしまう。

TDDを普及させるにはプロジェクトに余裕が必要

TDDを普及させるにはプロジェクトに余裕がないと難しい。 どうしても短納期だと最短パスで動くものを作ることになってしまう。

  • 会社の方針(教育)も重要。
  • 自社開発しているところだとTDD積極的にできそうですね。
    • 自社開発でもプレスリリースを出されちゃうと納期が決まるのでどうしてもウォーターフォールになってしまう。

設備について

  • 背もたれのついていない椅子を使うのは辛い。
  • 職場に外部ディスプレイがないときつい
    • 自分だけ持ち込んで使っている(猛者)

テストコードは納品するのか

  • そこは契約による。
  • QAのコードはTDDのコードとちょっと違う。

モック

27章のモックのコードは、モックの準備をしているだけ。

@Test
public void testOrderLookup() {
    Database db = new MockDatabase();
    db.expectQuery("SELECT order_no FROM Order WHERE cust_no = 123");
    db.returnResult(new String[] {"Order 2" ,"Order 3"});
    . . .
}

...以降このモックを利用したテストが続く。未経験者には理解が厳しいかもしれない。

  • モックは作るようなものなのか?
    • だいたいどの言語でも標準のモックが作られているからそちらを利用するのが推奨。
  • ファイルの入出力のモックはどうやって作れば良いのか?
    • プロダクションコードの方を引数をreade,writeのインターフェースを利用したメソッドにする
  • そもそもモックはなるべく使わない方が良い(必要最低限にするべき)
  • 外部に接続するような処理(URL)のモックだと、ローカルのディレクトリを使ったりする。

    equalsのテストは書くのか

  • IDEに生成させたロジックなどテスト不要では?
  • そもそもEqualsをIDEに作らせるのは危険
    • クラスに新しくメンバを追加した時、Equalsに反映されない。バグに繋がる恐れがある。
    • Javaは最近だとLombokなどを使う。

勉強会後、改めて思ったがEquals契約が守られているかを検証するテストをきちんと書こうとすると難しいと思った。

  • 反射性(reflexive): null以外の参照値xについて、x.equals(x)はtrueを返します。
  • 対称性(symmetric): null以外の参照値xおよびyについて、y.equals(x)がtrueを返す場合に限り、x.equals(y)はtrueを返します。
  • 推移性(transitive): null以外の参照値x、y、およびzについて、x.equals(y)がtrueを返し、y.equals(z)がtrueを返す場合、x.equals(z)はtrueを返します。
  • 一貫性(consistent): null以外の参照値xおよびyについて、x.equals(y)の複数の呼出しは、このオブジェクトに対するequalsによる比較で使われた情報が変更されていなければ、一貫してtrueを返すか、一貫してfalseを返します。
  • null以外の参照値xについて、x.equals(null)はfalseを返します。

https://docs.oracle.com/javase/jp/9/docs/api/java/lang/Object.html#equals-java.lang.Object-

標準ライブラリのテストは書くのか?

例:JQueryのparseJSONはJSON.parseをそのまま利用している。

https://github.com/jquery/jquery/commit/93a8fa6bfc1c8a469e188630b61e736dfb69e128

...
jQuery.isArray = Array.isArray;
jQuery.parseJSON = JSON.parse;
jQuery.nodeName = nodeName;
...

正規表現が使われている処理のテスト

  • OSS正規表現を使っているところのバグを見つけたことがある
  • 正規表現は念入りにテストしないとダメ
  • 誤った正規表現がよく使われている。例えば「.」をマッチさせたいために/./を使ってしまう等(正しくは/\./)
  • 正規表現で言語をパースするのはだめ。
    • 初期のphpperlを呼び出して正規表現でパースしていた気がする
      • 今はそんなことやっていないと思います。

わからなかったこと

トランザクショナルメモリの記述は何が言いたいのかわからない

参加者全員が頭を掲げることになりました。

ミュータブルなオブジェクトについての議論

ORマッパーやDSL,Rubyのrakeの話になり、どれも名前は知っているが 経験がないので話についていけませんでした。

Self Shunt

テストクラス自体がモックオブジェクトになるようなテスト実装のことらしいです。 サンプルがpythonで書かれているため、*3勉強会中に理解できませんでした。 これはPatternらしいのでJavaの実装をよく読んで理解したいです。

モック

自分のモックの理解が参加者のそれとずれてしまっているような不安を抱いてしまいました。 話を聞いてインターフェースをメソッドの引数の型にする。ということ以上のことがわかりませんでした。 ここは後述のTest Doubleを掘り下げていけばわかる気がします。

初心者おすすめ

勉強会に参加してやはり経験者との知識の壁を感じた。 まず入門書が読むべきドキュメントはありませんか、と質問したところ主催者のはむはむさんから、Test Doubleをオススメいただきました。

goyoki.hatenablog.com

感想

写経パート(第1部、第2部)が終わったため、コードを読むページが減り*4、実際のノウハウについての議論が多くなりました。

今までTDDは完全に絵空事でしたが、最近TDDを導入し始めたので、実践の悩みというものを持てるようになりました。 一人でやっているとわからないことだらけなので、こういう勉強会は本当に助かります。

テストは情報が多すぎてどこから手をつけていいのかわかりません。いつも英語で分厚い*5xUnit-Test Patternsにたどりついてしまいます*6

xUnit Test Patterns: Refactoring Test Code (Addison-Wesley Signature Series (Fowler))

xUnit Test Patterns: Refactoring Test Code (Addison-Wesley Signature Series (Fowler))

とりあえずオススメいただいたTest Doubleから着手しようと思います。

*1:請負をメインでやっている会社だと、案件で開発を効率化するツール(xUnit)を作りたいとなっても、 開発時間や予算を了承するのは困難を極めると思う。

*2:作ったことがある猛者もいるらしい

*3:私の母国語はJavaなのです。。。

*4:コードが乗っていると、どうしても実装の詳細に踏み込んでしまいがち。

*5:833ページ🙄

*6:この本は諸事情により翻訳が難しいそうです。