Javaのtest

http://d.hatena.ne.jp/mowamowa/20091126/1259188371

そういや、確かにprivateのテストは面倒で、
Javaは個人の趣味で書くくらいの私がよく使う手は以下3つくらい。
ほかの人たちはどうなんだろう。

とりあえずreflection

1.4以下と5.0以上で書き方は若干ちがう(var argsのせい)けど、基本は同じ。

// pkg1/Hoge.java
package pkg1;
public class Hoge {
  private void f(int x) {
  }
}
// pkg1/Hoge2.java
package pkg1;
import java.lang.reflect.Method;
public class Hoge2 {
  private static void f(Hoge h, int x) throws Exception {
    Method m = Hoge.class.getDeclaredMethod("f", Integer.TYPE);
    m.setAccessible(true);
    m.invoke(h, x);
  }

  public void test() throws Exception {
    Hoge h = new Hoge();
    f(h, 10);
  }
}

例外は必要に応じてTrapしておく。
一応Wrapするようなメソッドをつくっておかないと、Testで何をやってるかわからなくなりがち。
あと、関数signatureがかわっても、実行時にしかわからない。
まぁ、UnitTestとして使う分には問題ないはず。そもそも、がんがんまわしてるから気づくだろうし。
メリットはパッケージとか関係ないし、基本なんでもできる。


あとは、h.f(10)とf(h, 10)が同一視できるだけの心の目が必要。
Testのほうにもh.f(10)とか書きたいとすると、proxyをつくらなきゃいけなくなってかなり面倒なのでそこまではまずやらないなぁ。。。

package private方式

Testeeのクラスと同じパッケージにTestクラスを書く。
パッケージは同じだけど、ディレクトリは分けても大丈夫(というか分けないとカオスになる・・・)。


メリットはinterfaceを変えたときに、コンパイルエラーを出してくれるところ。
デメリットはTestじゃない同パッケージ内のクラスからも見えるようになること。
ライブラリにしたい場合は、たいていパッケージごとにリリースするはずなので、
大規模な人数で同じところを触らなければ、問題にはなりにくいかなぁ。

//  pkg1/Hoge.java 内
package pkg1;

public class Hoge {
  void f() {
    // ... (ry
  }
}
// test/pkg1/HogeTest.java 内
package pkg1;

public class HogeTest {
  public testF() {
    Hoge h = new Hoge();
    h.f();
  }
}

で、test直下で、

$ javac -cp .. pkg1/HogeTest.java

的な感じで。

そもそも全部publicにしてしまう

データを持っているクラスと、処理を行うクラスをばらばらにしてしまう。
処理を行うクラスは、単なるパラメータホルダーみたいにして、関数も全部publicにしてしまう。
で、どの順で関数を呼んでも不整合がないように作っておけば、そもそもprivateで悩まなくてもいい。


メリットは変な状態遷移が無いのでテストしやすいのと、
完成したコードの構造が綺麗に出来上がりやすい。


デメリットとしては、若干慣れるのに難があるのと、
綺麗にしすぎて、まれにパフォーマンスに影響がでる可能性があること。
あと、無理やりやろうとすると大変"素敵"なものができあがること。


くらいかなぁ。あとなんかあったべか。


使い分けは、case by caseで、なるべく3つめを心がけてるけど、
必要に応じて残り二つをつかう感じかな。


ちなみに、メソッドの追加もがんばれば可能なはずで(やったことないけど)、
VM/class fileの中身をしってれば、ClassLoaderをいじればよいはず。
たいていは現実てきじゃないのでjavassistとかつかうのかなぁ。