次のように「リンク切れ」の問題に対処しながら、責任の連鎖パターンを実装したいと思います。
public abstract class Handler{
private Handler m_successor;
public void setSuccessor(Handler successor)
{
m_successor = successor;
}
protected abstract boolean handleRequestImpl(Request request);
public final void handleRequest(Request request)
{
boolean handledByThisNode = this.handleRequestImpl(request);
if (m_successor != null && !handledByThisNode)
{
m_successor.handleRequest(request);
}
}
}
十分に一般的なアプローチのようです。しかし、保護された抽象メソッドでこれをどのようにテストできるのでしょうか? これを処理する方法は次のようです。
Handler
抽象メソッドを実装する のテスト専用サブクラスを実装します。これは、テストのメンテナンスには悪いようです。- 抽象メソッドの可視性をパブリックに変更しますが、テストに対応するために SUT を変更する必要はありません。
- 抽象クラスは、単体テストを必要としないほど十分に単純であると見なします。んー。
handleRequest
1 つ以上の具象サブクラスでメソッドの単体テストを実装します。しかし、これはテストを編成する賢明な方法とは思えません。- モックオブジェクトを使用する方法はありますか? Mockito を試してみましたが、保護された可視性を回避できないようです。
この種のテストの問題は、設計が間違っていることを意味し、継承ではなく構成を使用することをお勧めします[ 1 ]。私は今これを試していますが、このパターンの推奨される実装にこの問題があるのは奇妙に思えますが、単体テストに関するアドバイスは見つかりません。
更新: 示されているように、抽象クラスを依存関係の反転に置き換えました。これは、Mockito を使用して簡単にテストできるようになりました。まだ責任の連鎖のように見えます...何か足りないのですか?
// Implement a concrete class instead
public class ChainLink {
// Successor as before, but with new class type
private ChainLink m_successor;
// New type, RequestHandler
private RequestHandler m_handler;
// Constructor, with RequestHandler injected
public ChainLink(RequestHandler m_handler) {
this.m_handler = m_handler;
}
// Setter as before, but with new class type
public void setSuccessor(ChainLink successor) {
m_successor = successor;
}
public final void handleRequest(Request request) {
boolean handledByThisNode = m_handler.handleRequest(request);
if (m_successor != null && !handledByThisNode) {
m_successor.handleRequest(request);
}
}
}