Как реализовать цепочку ответственности тестируемым способом?

Я хотел бы реализовать шаблон цепочки ответственности, решающий проблему «разорванной ссылки» следующим образом:

 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);
     }
   }
 }

Похоже, подход достаточно распространенный. Но как это проверить с помощью защищенного абстрактного метода? Способы справиться с этим выглядят так:

  1. Реализуйте подкласс только для тестирования Handler , который реализует абстрактный метод. Это кажется плохим для тестового обслуживания.
  2. Измените видимость абстрактного метода на общедоступный, но мне не нужно менять SUT, чтобы приспособить тест.
  3. Считайте абстрактный класс достаточно простым, чтобы не требовать модульных тестов. Мммм.
  4. Реализуйте модульные тесты для метода handleRequest на одном или нескольких конкретных подклассах. Но это не кажется разумным способом организации тестов.
  5. Есть ли способ использовать фиктивный объект? Я пробовал 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);
    }
  }
}

6
задан MohanaRao SV 9 April 2019 в 11:41
поделиться