ひしだまの変更履歴

ひしだまHPの更新履歴。
主にTRPGリプレイの元ネタ集、プログラミング技術メモと自作ソフト、好きなゲームや音楽です。

先生、privateメソッドのテストがしたいです

2013-08-13 23:45:04 | PG(Java)

Javaでprivateメソッドをどうやってテストするかについて、不定期に話題にあがるんだけど。
「publicメソッドだけテストすればいい、privateメソッドをテストしたいというのはおかしい(設計(実装方法?)が間違っている)」という意見を聞いたことがあるが、違和感を持っていた。

で、「publicメソッドのテスト=仕様に基づくテスト=ブラックボックステスト」「privateメソッドをテストしたい=ホワイトボックステストをしたい」という事なのかもしれない、と思い付いてブログを書こうと思ってたんだけどDQ10が忙しくてさぼっていたら、
kencobaさんが『どうしてもprivateメンバをテストしたい人に対する、Unit4での解決案』でホワイトボックステスト・ブラックボックステストについて触れていた。(いやぁ、ブログをさぼると他の人が書いてくれて、ラッキー(爆))
その中で、「JUnitはブラックボックステストを前提としている」とあって、びっくり!なんと、そうだったのか!それならホワイトボックステストが出来なくても仕方ないなぁ
と思ったんだけど、ちょっと待って。後工程の結合試験や総合試験は、どうしたってブラックボックステストになるよね。先頭の単体試験(JUnit)でホワイトボックステストをしなかったら、どこでやるんだ…??

というところにタイムリーな記事が、渡辺 修司さんの『ブラックボックステストとホワイトボックステスト』。
「ユニットテスト(単体試験)はホワイトボックステストとブラックボックステストを組み合わせて行います」とのこと。
そりゃそーだよなー、ホワイトボックスとブラックボックスはテストの観点の話だから、工程とかあまり関係無さそうだもの。JUnitでホワイトボックステストをしても別に問題は無いわけだ。


ちなみに、自分がprivateメソッドのテストをしたいと思うのは、複雑な機能をメソッド分割して分離し、それぞれをテストしていきたいと思うから。
例えば以下の様なクラスを想定している。(どこぞのSIerっぽい雰囲気^^;)

public class ID0001Logic extends BusinessLogic {
  @Override
  public void execute(String key) {
    DAO dao = super.getDatabaseAccessObject();
    Record record = dao.selectByKey(key);
    record.setValue3(calculate(record.getValue1(), record.getValue2());
    dao.update(record);
  }

  private int calculate(int value1, int value2) {
    // 副作用の無いメソッド
    return ~; // 複雑な計算
  }
}

publicなのはexecuteメソッドだけだが、ロジックを切り出して、副作用の無いcalculateメソッドを作っている。calculateメソッドは他から呼ばれないので、普通の感覚ならprivateにするのは当然だろう。
でも、どう見ても、calculateをテストしたいと思うんだが、違うだろうか?
(もちろんexecuteメソッドもテスト対象だけど、テストパターンを色々実装する事を考えると、calculateメソッドを直接テストする方が遥かに楽) 


個人的には、こういうケースではpackage privateにする(可視修飾子を付けない)。

というか、オブジェクト指向はクラスを継承しメソッドをオーバーライドして部分的な修正を加えることで利便性(生産性)を上げるものだと思う。
が、フレームワークを作る人は可視範囲を制限しようとする。(カプセル化の考えからすれば当然なんだけど(苦笑))
フレームワークを利用する側(自分)としては、ちょっとだけ違う機能を加えたくて、オーバーライドできれば綺麗に作れるのに…ということが時々ある。(AntのCopyTaskとか、再利用したかったのに、出来ないし!)
そういう訳で、個人的にはprivateとかfinalとか使うの反対!(暴論w)
拡張する人の自己責任は当然なので、メソッドもフィールドも全部final無しのprotectedにしといてよ~(笑)

(先の例で言えば、calculateメソッドをprotectedにしておく(実際には、calculateメソッドが複雑であればさらにメソッド分割するはずなので、それらも全てprotectedにしておく)。すると、ID0001Logicを継承したサブクラスで一部のメソッドだけ変更して使える。これがprivateだと、コピペするしかなくなる(苦)(まぁ、どちらも自分の管理下にある場合は、オーバーライドしたくなった時にprotectedに変えればいいんだけどさ)) 


最後にもうひとつ、設計とテストの関係について。
テストは、設計書に書かれた内容が実現されているかを確認する為に行うものだと思う。
「要件定義書に書かれていることを確認する→総合試験」
「基本設計書に書かれていることを確認する→結合試験」
「詳細設計書に書かれていることを確認する→単体試験」といった感じ。
つまり、テストは本質的にブラックボックステストになる。

ここにもうひとつ、「プログラム設計書の確認→単体試験」という関係があると思っている。
プログラム設計書は、メソッドからロジックまで全部書かれている想定。しかし現在のプログラミング技法では、プログラム設計書は書かず、プログラムを直接記述する。
だから、この単体試験は、本来は「プログラム設計書を元とするブラックボックステスト」なのだが、実際は「プログラム設計書=プログラム」なので、プログラムを見て試験項目を抽出したテスト、すなわちホワイトボックステストになる。
言い直すと、「ホワイトボックステストは、プログラム設計書を元とするブラックボックステストである」。 

とも考えられると思うんだけど、どうかなぁ?

そしたら、privateメソッドも(本来はプログラム設計書に書かれているのでブラックボックステストの)テスト対象であり、「privateメソッドをテストしたいというのはおかしい」という話にはならないと思う。

コメント (4)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする