【问题标题】:Test an overridden method of an inner class with JUnit and Mockito使用 JUnit 和 Mockito 测试内部类的重写方法
【发布时间】:2020-01-16 04:38:36
【问题描述】:

我在需要测试的类中有一个方法。该方法使用我需要模拟的外部类,因此外部类不会被测试或执行其依赖项。特殊的挑战是:外部类的一个方法被覆盖。方法如下:

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

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

【问题讨论】:

标签: java mockito rx-java2 junit5


【解决方案1】:

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

假设您更改了类,而不是 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 方法。

现在在您的测试中,您可以简单地将 spy 传递给 dirscan 对象,然后 用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 来做你打算用覆盖方法做的事情对我来说似乎有点过头了。

真实的对象必须工作才能使这个测试有价值,所以你不妨测试真实的东西。

【讨论】:

  • 如果您绝对需要模拟DirScan,您仍然可以在scan 操作上使用doAnswer(假设它是void 方法)并从那里触发destination.onNext 调用。然而,这使测试看起来更加陌生。也许如果您可以提供更多关于DirScan 对象的哪些部分与getResult 方法交互的信息,则可能可以找出类的哪些部分需要模拟,因此不需要调用外部依赖项。
  • DirScan 类来自外部库并使用 JNI 调用来查询后端系统。在不可用的单元测试期间,我需要模拟 DirScan 以避免 C 调用。我会重构。谢谢指点
  • 在这种情况下,您的目标应该是替换那些 JNI 调用(或触发它们的方法)。在这种情况下使用spy 应该没问题,但是否可以这样做取决于实现。
  • DirScan 类及其 JNI 调用是我可以使用但不能更改的库
  • 没关系。对于间谍活动,您不需要更改代码,唯一的问题可能是 mockito 框架对模拟某些事物的限制。如果它是一个公共图书馆,我可以看看,看看它是否可能。
猜你喜欢
  • 2022-01-13
  • 2021-08-27
  • 1970-01-01
  • 2023-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多