ホソカワです。
on 00.9.25 1:11 PM, omura@....jp at omura@....jp
wrote:
…
> ◇
> テストのためだけのメソッドということで、私が最初に考えていたものは、
>
> テスト対象のクラスでしか実装することのできないようなメソッドであって
> 設計上、そのメソッドはpublic/protectedなインターフェイスとして不要であるもの
>
> なのかなと思っていました。
>
> save()については、別のメールでも書きましたが、そもそもテスト対象クラスのsave()
> を
> 関係のないテストで使うべきでないと思うので、ホソカワさんのヘルパクラス(このメ
> ール
> [XPJ-00917]前回は見落していました)のような方法を取るべきだと思っています。
>
>
> そう考えると、議論すべきだと私の思っているテストのためだけのメソッドというのは
> もっと限定されたものになるのです。
>
> テストというのは、対象のオブジェクトがある条件を満たす初期状態のとき、
> 一連のメソッドを実行した後での状態が、期待したものであるかどうかをチェックする
> ということだと思います。
>
> だから、対象クラスでテストのためだけのメソッドを作るとすると
>
> (1) public/protectedなメソッドだけでは認知できないオブジェクトの状態を知るため
> のメソッド
> (2) public/protectedなメソッドだけでは設定できないオブジェクトの状態を作り出す
> ためのメソッド
>
> くらいしか思い付きませんでした。
>
> 前者は、
> (1a) 対象クラスの設計者が、意図的にそのメソッドをアクセス不能とする場合や、
> (1b) publicなメソッドで認知されるそのクラスのモデル(*1)のようなものと、実装の
> モデルとが
> 異なっていて、実装のほうは見せないようにする場合
> などがあると思います。
>
> (*1 これはクラスのメンタル・モデルと読んでもいいのかなと思っています。だとする
> と、機能仕様と
> いうのはシステムのメンタル・モデルの記述ということになる..余談)
>
> 後者は、
> (2a) publicなメソッドを使えば、なんとか状態を作れるけれど、面倒くさいので一気
> にやるメソッドがほしい場合とか
テスト側で、「一気にやってしまう」メソッドを書けると思います。
> (2b) そもそもそういう状態は作れない場合とか
> があるのかなと思います。
>
> (ここ、かなりいいかげんなので、間違い、指摘していただけるとありがたいと思いま
> す)
例があれば、分かりやすいと思いますが、クラスを公開されているメソッドのみ使っ
て作れない状態をテストする必要があるのでしょうか?
>
> そういうことを考えていたので、最終兵器、reflectionを使えば、そういうメソッドを
> 作る必要はないなということも書きました。
>
私は、(1) の get型の「テストのためだけのメソッド」はあると思いますが、(2) の
set型は、ないのではないでしょうか?
> で、細かいことをいい始めると長くなるのではしょってしまつて、一番、問題になると
> 思ったのは、
> インターフェイスのモデルと実装のモデルが違うときに、何をテストすればいいのかな
> ということです。
>
そう!このようなことですね、テストのためだけのメソッドが必要になってくるわけ
は。
> 実装のテストを直接しようとすると、インターフェイスとはずれた内部状態を見る必要
> がでてきて、
> そのためのメソッドを作らなくてはならなくなりますが、そのメソッドは設計上の本来
> のインターフェイス
> を壊すので、いやですよね。
>
> で、
>
> ・ XPでは、そんなねじれた設計はしないのではないかと思いますがどうでしょう。
>
Do The Simplest Thing That Could Possibly Work (DTSTTCPW) を実践すれば、可能
かと思います。でも、インターフェイスとインプリがずれているコードはあるでしょ
う。
> ・ もし、そういうことをした場合、たとえば、こんなふうにしてテストする。
> つまり、実装のクラスを別途作成し、対象のクラスだったものは、実装のクラスから
> そのインターフェイスへの
> 変換だけを行うものとする。こうすることで、実装のクラスののテストの後、実装の
> クラスからインターフェイスの
> クラスへの変換のテストを行えば、中身を見なくてもなんとかなる...ちょっと大変
> なのは、そもそも設計が
> ねじれているからなので、仕方がないと考える。
>
矢崎さんの SqlHandler
>> public class SqlHandler {
>> private String command;
>>
>> SqlHandler(String command) {
>> this.command = command; // テストできない
>> }
>> }
をこのスタイルで書いてみましたが、…
public class SqlHandler {
private SqlCommand sqlCommand;
SqlHandler(String command) {
sqlCommand = new SqlCommand(command);
}
}
public class SqlCommand {
private String command;
SqlCommand(String command) {
this.command = command; // 相変わらず、テストできない
}
}
この様に書くと問題は解決されません。SqlCommand クラスをテストするために
getCommand() が欲しくなります。
ただ、後で、getCommand() は、必ず必要になってくるメソッドだと思います。(SQL
データベースに対してコマンドを発行する時、コマンド文字列を SqlCommand から取
得するでしょう。)それに、SqlHandler クラスに入っているよりは、SqlCommand ク
ラスのほうがしっくりきます。
私の案で、/* testuse */ を付けると、このようになります。
public class SqlCommand {
private String command;
SqlCommand(String command) {
this.command = command;
}
/* testuse */ public String getCommand() {
return command;
}
}
# それから、Java の場合、interface を使うと、インターフェースとインプリをわ
けることもできますね。
>
> こういう問題意識自体が、ずれているのかなあとも思えてきたんで、議論の行末をみて
> いたいと
> 思います。
一歩、前進した感じがします。
--
Kaoru Hosokawa
khosokawa@....com