使用 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,使其成为依赖项或参数。或者,您可以使用 PowerMockito
的 whenNew
功能。
让我们假设您更改 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
来做你打算用覆盖方法做的事情对我来说似乎有点过分了。
实物必须工作,这个测试才有价值,所以你不妨测试一下真实的东西。
我在 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,使其成为依赖项或参数。或者,您可以使用 PowerMockito
的 whenNew
功能。
让我们假设您更改 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
来做你打算用覆盖方法做的事情对我来说似乎有点过分了。
实物必须工作,这个测试才有价值,所以你不妨测试一下真实的东西。