我有一个类似的课程:
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进行测试......
答案 0 :(得分: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
也在运行命令。你需要解耦那些(运行命令,保持它们和跟踪状态)。也许在命令本身内封装命令的状态。因此命令将知道如何以自己的方式工作。