2017-08-24 49 views
0

我有一个类似的类:如何重构类,以便可测试没有反射

public class QueueingCommandRunner { 
    private Status status; 
    private Map<Class<CommandHandler>, CommandHandler> queuedCommands; 
    private RunnerClass runnerClass; 
    private ExternalCommandRunnerRegistry externalCommandRunnerRegistry; 
    private ExternalCommandRunner externalCommandRunner; 

    public QueueingCommandRunner(ExternalCommandRunnerRegistry externalCommandRunnerRegistry, 
      RunnerClass runnerClass) { 

     this.externalCommandRunnerRegistry = externalCommandRunnerRegistry; 
     this.runnerClass = runnerClass; 
     this.queuedCommands = new LinkedHashMap<>(); 
     this.status = Status.DOWN; 
    } 

    public void init() { 
     doSomeStuff(); 
     externalCommandRunner = externalCommandRunnerRegistry.get(runnerClass); 
     externalCommandRunner.runListeningCommand(ListenableStatusCommand.class, 
       new ListenableStatusHandler(this::changeStatus)); 
    } 

    public <T extends CommandHandler> void runCommand(Class<T> command, T commandHandler) { 
     if (status == UP) { 
      externalCommandRunner.run(command, commandHandler); 
     } else { 
      queuedCommands.put(command, commandHandler); 
     } 
    } 

    private void changeStatus(Status status) { 
     this.status = status; 
     if (status == UP) { 
      Iterator<Entry<Class<CommandHandler>, CommandHandler>> commandsIterator = 
       queuedCommands.iterator(); 
      while (commandsIterator.hasNext()) { 
       <Entry<Class<CommandHandler>, CommandHandler>> queuedCommand = commandsIterator.next(); 
       externalCommandRunner.run(queuedCommand.getKey(), queuedCommand.getValue()); 
       commandsIterator.remove(); 
      } 
     } 
    } 
} 

我忽略的东西一样同步。我的问题是,如何测试内部排队而不使用诸如通过反射调用私有方法之类的东西?特别是我想知道如何测试changeStatus方法,因为它不是直接从这个类的任何公共方法运行。这个类是否由于设计而不好(从单元测试的角度来看)?

我使用的测试JMockit ...

+4

单元测试时,您验证**所需的公共可观察行为**,即*返回值*和*与依赖关系*的通信与提供的输入有关。被测代码内部发生的任何事情都是*实现细节*,并未经过测试(直接)。这使您有机会在不改变测试的情况下将这些*实现细节*更改为更高效或更为理想的方法。 –

回答

1

正如在注释中 - 你测试

期望公众看得见的行为

所以,如果你想测试私人方法,你需要让他们公开。我建议,使其作为接口:

public interface SomeInterface { 

    changeStatus(Status status); 
} 

然后注入实施类:

public final class A { 

    private final SomeInterface someInterface; 

    public A(SomeInterface someInterface) { 
     this.someInterface = someInterface; 
    } 
} 

然后你就可以轻松地测试SomeInterface执行和模拟,如果你在你的A类需要它。

所以,我不能给你整个你的特定情况的重构过程。但是你可以遵循这个指导原则,你可以用所有容易测试的接口封装所有的私有方法。正如我所看到的,您正在使用私有方法使用您的类的内部细节 - 这些细节应该封装在接口实现构造函数中(通过另一个接口),并且最终会得到一些小巧的可凝聚的可测试类。看看Command Pattern,因为它看起来适合你的情况,并尝试按照SOLID这也将导致可测试的代码。

我看到一些问题与您的设计:

  • init()方法。这会导致时间耦合,因为你的班级在施工后还没有准备好使用;
  • 你的runCommand方法是基于状态做两件事。它可以是运行命令,也可以放在地图上(这是隐藏的副作用);
  • 您的changeStatus也正在运行命令。

你需要去耦这些变量(运行命令,保持它们和跟踪状态)。也许封装命令内部命令的状态。所以这个命令会知道如何以自己的方式工作。

相关问题