使用 JUnit 和 Mockito 测试内部 class 的覆盖方法

Test an overridden method of an inner class with JUnit and Mockito

我在 class 中有一个方法需要测试。该方法使用了我需要模拟的外部 class,因此外部 class 未经过测试或执行其依赖项。特殊的挑战是:外部 class 的一种方法被覆盖。方法如下所示:

public void fetchLocalData(final String source, final ObservableEmitter<String> destination) {
   final List<String> options = Arrays.asList("recursive","allFiles","includeDir");
   // This class comes from a package
   final DirScan dirscan = new DirScan(source, options) {
       @Override
       protected Action getResult(final String result) {
           destination.onNext(result);
           return Action.Continue;
       }
   };
   dirscan.scan();
   destination.onComplete();
}

我试过了:

    DirScan scanner = mock(DirScan.class);
    when(scanner.scan()).thenReturn("one").thenReturn("two");

那没用。我想念什么?我需要如何重构才能使其可测试?

如果您想用模拟(或间谍)替换 dirscan,您需要重构 class,使其成为依赖项或参数。或者,您可以使用 PowerMockitowhenNew 功能。

让我们假设您更改 class 而不是 String source 您提供 DirScan 对象作为参数。您需要在其他地方为 dirscan 创建某种创建方法(可能是 static 方法)。

final List<String> options = Arrays.asList("recursive","allFiles","includeDir");

public DirScan createDirScan(String source) {

   // This class comes from a package
   final DirScan dirscan = new DirScan(source, options) {
       @Override
       protected Action getResult(final String result) {
           destination.onNext(result);
           return Action.Continue;
       }
   };

   return dirscan;
}
public void fetchLocalData(final DirScan dirscan, final ObservableEmitter<String> destination) {
    dirscan.scan();
    destination.onComplete();
}

从你的问题来看,你似乎想测试与目标对象的交互,所以你不想模拟 dirscan 对象(因为如果你这样做就不会有任何交互)。您可能想使用 spy 并且仅替换 getResult 方法。

在你的测试中,现在你可以简单地为 dirscan 对象传递一个 spy 并且 用 thenAnswer.

定义它的行为
final ObservableEmitter<String> destination = ...

DirScan dirscan = Mockito.spy(createDirScan(source, destination));

Mockito.when(dirscan.getResult(Mockito.any(String.class))).thenAnswer((Answer<Action>) invocation -> {
    String result = invocation.getArgument(0);
    destination.onNext(result);

    return Action.Continue;
});

classUnderTest.fetchLocalData(dirscan, destination);

此时您可能会注意到, 使用间谍并只使用真正的 DirScan 对象可能更好。使用 spy 来做你打算用覆盖方法做的事情对我来说似乎有点过分了。

实物必须工作,这个测试才有价值,所以你不妨测试一下真实的东西。