9章このクラスをテストハーネスに入れることができません
テストしたいクラスがあります。しかし、テストケース内でインスタンス生成が難しい。その状況ごとにどのように対応すればいいかを説明しています。
大きく分けると5パターン
//(1)コンストラクタ注入のオブジェクトの生成が難しい
public void testFuga() {
SomeConnection conn = new SomeConnection();
Fuga fuga = new Fuga(conn);
}
// (2)クラス内で、newしているオブジェクトの生成が難しい
public void testFuga() {
Fuga fuga = new Fuga();
}
class Fuga {
public Fuga() {
this.conn = new SomeConnection();
}
}
// (3)Singletonへアクセスしている
class Fuga {
public Fuga() {
this.hoge = MySingleton.getInstance();
}
}
// (4)条件によって、親クラス、子クラスを使い分けているプロパティがある
// BaseHoge、MyHogeとクラス階層があるため、IMyHogeインターフェースを作っても、BaseHoge型に代入できない。
class Fuga {
public Fuga() {
if (someFlag) {
this.hoge = new BaseHoge();
} else {
this.hoge = new MyHoge();
}
}
}
class MyHoge extends BaseHoge {
}
// (5)C++ の #include依存関係
Code language: Java (java)
コンストラクタ注入のオブジェクト | ← | クラス内で、newしているオブジェクト | ← | Singleton | 親クラス、子クラスを使い分けている | |
---|---|---|---|---|---|---|
9.1 いらだたしいパラメータ | 9.6 玉ねぎパラメータ | 9.2 隠れた依存関係 | 9.3 複雑な生成 | 9.4 いらだたしいグローバルな依存関係 | 9.7 別名のパラメータ | |
この章で説明あり | ||||||
インターフェースの抽出(377) | ● | ● | ● | ● | ● | |
Nullを渡す(123)Nullオブジェクト(124) | ● | ● | ||||
サブクラス化とメソッドのオーバーライド(415) | ● | ● | ● | |||
コンストラクタのパラメータ化(394)+シグネチャの維持(328) | ● | ● | ||||
インスタンス変数の入れ替え(418) | ● | ● | ||||
静的setメソッドの導入(386) | ● | |||||
この章で説明なし | ||||||
getメソッドの抽出とオーバーライド(368) | ● | |||||
FactoryMethodの抽出とオーバーライド(366) | ● | ● | ||||
実装の抽出(371) | ● | ● | ● | |||
メソッドのパラメータ化(398) | ● | |||||
呼び出しの抽出とオーバーライド(363) | ● |
9.7 別名のパラメータ
それは、コンストラクタに対して渡すべきOriginationPermitを作ることが困難なことです。OriginationPermitは恐るべき依存関係を持っています。
p147
OriginationPermitのインターフェース抽出して、フェイクをコンストラクタ注入したい、だけど、それは難しいって言っているけど、よくわからないよ
次のような状況よね
// テスト対象
public class lndustrialFacilityextendsFacility
{
Permit basePermit;
public lndustrialFacilityextendsFacility(Permit originationPermit) {
if (flag) {
permit = originationPermit
} else {
permit = new Permit();
}
}
}
// Permitのクラス階層
class Permit
{
}
class FacilityPermit extends Permit
{
}
class OriginationPermit extends FacilityPermit
{
}
Code language: Java (java)
IOriginationPermitインタフェース型をPermit型のフィールドに代入しなければなりませんが、それは機能しないでしょう。なぜならJavaでは、インタフェースがクラスを継承できないためです。
p148
むむ?
やってみましょ
// インターフェースを定義して
interface IOriginationPermit
{
}
// Fakeクラスを定義して
// (FacilityPermitクラスやPermitクラスを継承していないことに注目)
class FakeOriginationPermit implements IOriginationPermit
{
}
// テストケース
// Fakeクラスのインスタンスを渡そうとすると、コンパイルエラー
void test() {
new lndustrialFacilityextendsFacility(
new FakeOriginationPermit() // ★ココで、java: 不適合な型: <anonymous IOriginationPermit>をPermitに変換できません:
);
}
Code language: Java (java)
コンパイルエラーになっちゃったよ...
FakeOriginationPermitクラスは、Permitクラスを継承していないからね
最もわかりやすい解決方法は、すべてをたどってそれぞれにインタフェースを作成し、PermitのフィールドをIPermitのフィールドに変えることです。図9.5はこの状況を表します。これはとんでもない量の仕事になりますし、私はコードをこんな風にしてしまうことを好みません。
p148
...
しかし他の選択肢があるのなら、それを検討すべきです。
どういうこと?
クラス階層に対応した、インターフェース階層を用意するってことね
// インターフェースの継承階層を定義して
interface IPermit
{
}
interface IFacilityPermit extends IPermit
{
}
interface IOriginationPermit extends IFacilityPermit
{
}
// 元のクラスにインターフェースを定義して
class Permit implements IPermit
{
}
class FacilityPermit extends Permit implements IFacilityPermit
{
}
class OriginationPermit extends FacilityPermit implements IFacilityPermit
{
}
// テスト対象の Permit型 を IPermit に変更する
public class lndustrialFacilityextendsFacility
{
IPermit basePermit;
}
Code language: Java (java)
面倒そうだね...
この場合は「サブクラス化とメソッドのオーバーライド」を使えたそうよ
public class OriginationPermit extends FacilityPermit
{
public void validate() {
// データベースに接続する
// 許可情報を問い合わせる
// 許可フラグをセットする
// データベースを閉じる
}
}
Code language: Java (java)
テストケース内で、このvalidateメソッドが呼ばれるらしいんだけど、データベースには接続したくないのね
まず、abstractなFakeクラスを定義しておいて
(サブクラス化)
public abstract class FakeOriginationPermit extends OriginationPermit
{
abstract public void validate();
}
Code language: Java (java)
その場で必要なvalidateメソッドを持つインスタンスを作るの
(メソッドのオーバーライド)
public void testHasPermits() {
class AlwaysValidPermit extends FakeOriginationPermit
{
public void validate() {
// 許可フラグをセットする(データベースには接続しない)
}
};
Facility facility = new lndustrialFacility(new AlwaysValidPermit());
}
Code language: Java (java)
苦労して OriginationPermitクラスのFakeを用意したけど、本当にテストしたいのは、IndustrialFacilityクラスなんだよね。頭がこんがらがっちゃうよ...