[英]Java Mockito stuck at doReturn of singleton method
I have a singleton class to help me reading input from the console: 我有一个单例课程来帮助我从控制台读取输入:
public class IOHelper {
public org.slf4j.Logger logger = Logger.logger;
//JLine
public ConsoleReader cr;
private static IOHelper instance;
private IOHelper(){
{
try {
cr = new ConsoleReader();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static synchronized IOHelper getInstance(){
if (instance == null){
instance = new IOHelper();
}
return instance;
}
The code which I'd like to test refers to it as: 我要测试的代码将其称为:
String in = IOHelper.getInstance().cr.readLine();
Then my test class: 然后我的测试课:
class Test {
private static NetworkCommunicator networkCommunicator;
private static IOHelper ioHelper;
@BeforeAll
static void setUpClass() throws Throwable {
ioHelper = spy(IOHelper.getInstance());
doReturn("1").when(ioHelper).cr.readLine();
networkCommunicator = spy(NetworkCommunicator.class);
doNothing().when(networkCommunicator).connectToServer();
doNothing().when(networkCommunicator).connectToOtherServer();
}
My test gets stuck on the doReturn("1").when(ioHelper).cr.readLine();
我的测试卡在了
doReturn("1").when(ioHelper).cr.readLine();
line as if it actually executed the cr.readline();
行,就好像它实际执行了
cr.readline();
part. 部分。 My stacktrace points towards the method
private native int read0() throws IOException;
我的
private native int read0() throws IOException;
指向private native int read0() throws IOException;
found on FileInputStream. 在FileInputStream上找到。 The comments suggest it blocks if no input is available.
注释表明,如果没有可用的输入,它将阻止。 I want to replace the method
readLine()
on my console, so when my CLI asks for an input, my test can "fake" that input. 我想替换控制台上的
readLine()
方法,因此当CLI要求输入时,测试可以“伪造”该输入。
edit: the call stack of the 2 interesting threads: 编辑:2个有趣的线程的调用堆栈:
"main@1" prio=5 tid=0x1 nid=NA runnable
java.lang.Thread.State: RUNNABLE
blocks NonBlockingInputStreamThread@1437
at java.io.FileInputStream.read0(FileInputStream.java:-1)
at java.io.FileInputStream.read(FileInputStream.java:207)
at jline.internal.NonBlockingInputStream.read(NonBlockingInputStream.java:166)
- locked <0x67d> (a jline.internal.NonBlockingInputStream)
at jline.internal.NonBlockingInputStream.read(NonBlockingInputStream.java:135)
at jline.internal.NonBlockingInputStream.read(NonBlockingInputStream.java:243)
at jline.internal.InputStreamReader.read(InputStreamReader.java:257)
at jline.internal.InputStreamReader.read(InputStreamReader.java:194)
at jline.console.ConsoleReader.readCharacter(ConsoleReader.java:2147)
at jline.console.ConsoleReader.readCharacter(ConsoleReader.java:2137)
at jline.console.ConsoleReader.readBinding(ConsoleReader.java:2222)
at jline.console.ConsoleReader.readLine(ConsoleReader.java:2463)
at jline.console.ConsoleReader.readLine(ConsoleReader.java:2374)
at jline.console.ConsoleReader.readLine(ConsoleReader.java:2362)
at jline.console.ConsoleReader.readLine(ConsoleReader.java:2350)
at com.mypkg.Test.setUpClass(Test.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:389)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$invokeBeforeAllMethods$5(ClassTestDescriptor.java:228)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor$$Lambda$162.715378067.execute(Unknown Source:-1)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.invokeBeforeAllMethods(ClassTestDescriptor.java:227)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.before(ClassTestDescriptor.java:151)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.before(ClassTestDescriptor.java:61)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute$3(HierarchicalTestExecutor.java:80)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$$Lambda$134.398690014.execute(Unknown Source:-1)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:77)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$null$2(HierarchicalTestExecutor.java:92)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$$Lambda$137.1353170030.accept(Unknown Source:-1)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute$3(HierarchicalTestExecutor.java:92)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$$Lambda$134.398690014.execute(Unknown Source:-1)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:77)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:51)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:62)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
"NonBlockingInputStreamThread@1437" daemon prio=5 tid=0xf nid=NA waiting
java.lang.Thread.State: WAITING
waiting for main@1 to release lock on <0x67d> (a jline.internal.NonBlockingInputStream)
at java.lang.Object.wait(Object.java:-1)
at jline.internal.NonBlockingInputStream.run(NonBlockingInputStream.java:275)
at java.lang.Thread.run(Thread.java:745)
Extending this question: I have some methods which asks the user multiple inputs (eg. updating some settings). 扩展这个问题:我有一些方法向用户询问多个输入(例如,更新某些设置)。 Am I correct to think that the best approach would be to refactor the settings to a method which takes arguments and test only this new method?
我是否认为最好的方法是将设置重构为带有参数并仅测试该新方法的方法? Is there a solution where I could just pass down a series of strings to the test to press when any method is trying to read from the
ConsoleReader
? 有一种解决方案,当尝试从
ConsoleReader
读取任何方法时,我可以将一系列字符串传递给测试按ConsoleReader
? I thought of using Robot
but how can I make sure that it passes down the keystrokes in a correct order if the reading is not done by the test rather than the underlying logic? 我曾考虑过使用
Robot
但是如果不是通过测试而不是通过底层的逻辑来进行读取,那么如何确保以正确的顺序传递击键呢?
Seems like you're mocking the wrong thing. 似乎您在嘲笑错误的事情。 You're wanting to play with the
ConsoleReader
's returns. 您想使用
ConsoleReader
的收益表。 So a couple of options: 所以有两个选择:
Move to use some getConsoleReader()
method on your IOHelper
class, which you can then mock out - you'll need to make sure that the IOHelper
class also accesses this via this method. 移至在
IOHelper
类上使用一些getConsoleReader()
方法,然后可以将其模拟出来-您需要确保IOHelper
类也通过此方法访问此方法。 Eg 例如
private final IOHelper mySpy = spy(IOHelper.getInstance());
@Before
public void setup() {
final ConsoleReader mockCR = mock(ConsoleReader.class);
// Any mockery on your mockCR you need.
// doReturn(...).when(mockCR).readLine();, etc.
doReturn(mockCR).when(mySpy).getConsoleReader();
}
Modify the cr field to be a mock. 将cr字段修改为模拟。 Eg
例如
private final IOHelper ioHelper= IOHelper.getInstance();
@Before
public void setup() {
final ConsoleReader mockCR = mock(ConsoleReader.class);
// Any mockery on your mockCR you need.
// doReturn(...).when(mockCR).readLine();, etc.
ioHelper.cr = mockCR;
}
I'd warn against this case though; 但是我警告过这种情况。 I can't see a reason to have your
ConsoleReader
be public
(or not be final
), and this just makes that a requirement. 我看不出将您的
ConsoleReader
public
(或不是final
)的理由,而这仅是一项要求。 You can always use some helper library to mess with the field even if it's private. 您可以始终使用一些帮助程序库来弄乱该字段,即使该字段是私有的也是如此。 spring and apache-commons-lang3 both provide this type of utility.
spring和apache-commons-lang3均提供此类实用程序。
Use Powermock to fiddle with the constructor of ConsoleReader
: 使用Powermock摆弄ConsoleReader的构造
ConsoleReader
:
@RunWith(PowerMockRunner.class)
@PrepareForTest(IOHelper.class)
public class IOHelperTest {
@BeforeClass
public static void setup() {
final ConsoleReader mockCR = mock(ConsoleReader.class);
// Any mockery on your mockCR you need.
// doReturn(...).when(mockCR).readLine();, etc.
PowerMock.whenNew(ConsoleReader.class).thenReturn(mockCR);
}
}
Finally, you could modify your IOHelper
class to take a ConsoleReader
as a constructor argument, and simply provide the mockCR
from all the above approaches to that (and make it not private
). 最后,您可以修改
IOHelper
类,以将ConsoleReader
用作构造函数参数,并只需通过上述所有方法来提供mockCR
即可(并使其不private
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.