2016-03-20 11 views
1

コード:ユニットテスト

public class DamselRescuingKnight implements Knight { 
    private RescueDamselQuest quest; 
    public DamselRescuingKnight() { 
     this.quest = new RescueDamselQuest(); 
    } 
    public void embarkOnQuest() { 
     quest.embark(); 
    } 
} 

public class BraveKnight implements Knight { 
    private Quest quest; 
    public BraveKnight(Quest quest) { 
     this.quest = quest; 
    } 
    public void embarkOnQuest() { 
     quest.embark(); 
    } 
} 


public class BraveKnightTest { 
    @Test 
    public void knightShouldEmbarkOnQuest() { 
     Quest mockQuest = mock(Quest.class); 
     BraveKnight knight = new BraveKnight(mockQuest); 
     knight.embarkOnQuest(); 
     verify(mockQuest, times(1)).embark(); 
    } 
} 

私たちが依存するコードを変更せずに実装を切り替えることができます依存性注入を使用することを、理解しています。

本書では、「ユニットテストを書くのが非常に難しい...」と書かれています。

しかし、私はどのように依存性注入なしで単体テストのために非常に難しいか理解できません!私の直感は協力しません!

「DamselRescuingKnight」クラスと他の優れたサンプルクラス(DIなし)のjunit/unitテストを書くことができます。これにより、DIがユニットテストを容易にするポイント/ステージを実現できますか?

+1

あなたのコードはとてもシンプルなので、本当に説明するのは難しいです。 quest.embarkが複雑なコードで、データベースにアクセスし、ユーザー入力と複数のコンピュータが関係する場合は、複雑なコードをより簡単なものに置き換えることをお勧めします。しかし、あなたのコードでは単体テストでさえ意味がありません。 quest.embarkはテストする必要がありますが、ここにあるコードはテストしません。 –

答えて

1

問題はもちろんquestです。あなたは何とかembark()メソッドが呼び出されたことを確認したいです。それを模倣したインスタンスに置き換えることができなければ、これは非常に困難です。

privateの代わりに変数がprotectedの場合、テストケースは同じパッケージ内に存在するため、上書きすることができます。

また、アスペクト指向プログラミングを使用して変数を置き換えることもできます。

しかし、コードがget-goからの依存性注入で書かれているのが最も簡単です。

あなたはどのようにAOPを使用することができますかを確認する必要があります。以下は、ユニットテストでのインスタンスをMockRescueDamselQuestと名付けられた模倣したものに置き換えるために使用できるAspectJのポイントカットの例です(私が正しい構文を得られない場合は謝罪します、AspectJ ):

aspect MockRescueDamselQuestInstantiations { 
    RescueDamselQuest around(): call(RescueDamselQuest.new()) { 
     return new MockRescueDamselQuest(); 
    } 
} 

これはRescueDamselQuestの任意のインスタンスをキャッチ(すなわちnew RescueDamselQuest()への呼び出し)、代わりにMockRescueDamselQuestオブジェクトを返します。

これ以上の配線が必要な場合は、代わりに依存関係注入を使用することを強く推奨します。

+0

テストする唯一の妥当なことは、 'embarkOnQuest()'が呼び出されたときに 'quest.embark()'が呼び出されたかどうかをチェックすることです。このシナリオでは@Jamesが指しています。あなたは嘲笑された事例がなければそれはとても難しいと言います。あなたはやってみて、それが難しいことを示すことができますか?それは私に視点を与えるでしょう。どういうわけか私が何らかの形で故意にDIを避けたら、それはAOPで行うことができると言います。あなたはそのような例を指摘できますか? – user104309

3

上記の例の難易度は、DamselRescuingKnightをテストしようとしたときに発生します。

public class DamselRescuingKnight implements Knight { 
    private RescueDamselQuest quest; 
    public DamselRescuingKnight() { 
     this.quest = new RescueDamselQuest(); 
    } 
    public void embarkOnQuest() { 
     quest.embark(); 
    } 
} 


public class DamselRescuingKnight Test { 
    @Test 
    public void knightShouldEmbarkOnQuest() { 
     DamselRescuingKnight knight = new DamselRescuingKnight(); 
     knight.embarkOnQuest(); 
     // now what?    
    } 
} 

どのようにknight.embarkOnQuest()は実際に何もしないことを確認することができます(下記参照)を使用すると、1つをテストしたい、と仮定?答えは、内部的に使用するクエストインスタンスにアクセスできないため、できないということです。

このようなクラスをテストするには、getQuest()メソッドをKnightに追加し、isEmbarked()メソッドをQuestに追加します。 ナイトはパラメータなしでクエストを呼び出すだけなので、この例は非常に簡単だと言っても大丈夫です。騎士がクエストとやりとりし、鍛冶屋から武器を手に入れたら、どうにかしてそのアクセスを許可する必要があります。あなたはおそらくそれを行うためにすべての定型文を行うことができます。しかし、あなたは鍛冶屋にパラメータを渡していると仮定します - 渡されたパラメータが正しいことをどのように確認しますか?または、騎士が武器を取得する方法をの前に確認するには、のクエストに行くのですか?

これは依存性注入が救助に来るところです。モックフレームワークを使用するか、独自のモックを実装するだけで、モックを作成して、あなたのナイトが期待していることを検証できるようにすることができます。