简体   繁体   English

Jmock-如何自动化和模拟控制台用户输入?

[英]Jmock - how to automate & mock out console user input?

I have some functionality that I want to mock out being called from main (static: I've read about that too - jmock mocking a static method ). 我有一些功能,我想模拟一下从main调用的功能(静态:我也读过有关此内容-jmock模拟静态方法 )。 i recently read that JMock doesn't support the mocking of static functions. 我最近读到JMock不支持静态函数的模拟。 Well, the associated code (that's giving me a problem) must be called from main, and must be in the class with main... 好吧,相关的代码(这给我带来了一个问题)必须从main调用,并且必须在具有main的类中。

Sample source 样本来源

Test code 测试码

Right now, I want to ensure that my main has a test to make sure that the file exists before it proceeds. 现在,我想确保我的主体进行了测试,以确保文件在继续之前存在。 Problem is, I have my program getting user input from the console, so I don't know how to mock that out? 问题是,我的程序从控制台获取用户输入,所以我不知道该如何模拟? Do I just go down to that level of granularity, specifying at every point along the way what happens, so that I can write about only one operation in a function that returns the user's input? 我是否只是降低了粒度级别,在此过程中的每一点都指定了发生的事情,以便我只能在返回用户输入的函数中编写一个操作? I know that to write the tests well, when the tests are run, they should not ask for the user input, I should be specifying it in my tests somehow. 我知道编写好的测试,在运行测试时,他们不应该要求用户输入,我应该以某种方式在测试中指定它。

I think it has to do with the following: How to use JMock to test mocked methods inside a mocked method I'm not that good with JMock... 我认为这与以下内容有关: 如何使用JMock在模拟方法中测试模拟方法我对JMock不太满意...

If the readInput() method does something, like, say: 如果readInput()方法执行某些操作,例如:

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
return in.readLine();

Then you might be able to get away with a test that goes something like: 这样一来,您也许可以摆脱类似这样的测试:

InputStream oldSystemIn = System.in;
InputStream mockSystemIn = context.mock(InputStream.class);
System.setIn(mockSystemIn);
context.checking(new Expectations() {{
    // mock expected method calls and return values
}});
// execute
// verify
System.setIn(oldSystemIn);

You can use System Rules instead of mocking System.out and System.in. 您可以使用系统规则,而不是模拟System.out和System.in。

public void MyTest {
  @Rule
  public TextFromStandardInputStream systemInMock = emptyStandardInputStream();

  @Test
  public void readTextFromStandardInputStream() {
    systemInMock.provideText("your file name");
    //your code that reads "your file name" from System.in
  }
}

Stefan Birkner's answer gave me the direction that I need to be able to solve this. Stefan Birkner的回答为我提供了解决此问题所需的方向。 I have posted the code that I used to solve this below. 我已经在下面发布了用于解决此问题的代码。

Solved tests: Birkner's version ( recommended ) 解决的测试:Birkner版本( 推荐

Solved tests: piped version 解决的测试:管道版本

Changed source: 更改的来源:

WHY : What happens is, with Birkner's library, you can only ever read as much input as you instantiate with the rule originally. 为什么 :发生的情况是,使用Birkner的库,您只能读取与最初实例化该规则一样多的输入。 If you want to iteratively write to the endpoint, you can do this with a pipe hack, but it doesn't make much of a difference, you can't write to the input over the pipe while the function is actually running, so you might as well use Birkner's version, his @Rule is more concise. 如果要迭代地写入端点,则可以通过管道破解来做到这一点,但这并没有多大区别,您不能在函数实际运行时通过管道写入输入,因此您可以最好使用Birkner的版本,他的@Rule更简洁。

Explanation : In both the pipe hack and with Birkner's code, in the client being tested, multiple calls to create any object that reads from System.in will cause a blocking problem where, once the first object has opened a connection to the Pipe or to System.in, others can not. 说明 :在管道hack和使用Birkner的代码的情况下,在要测试的客户端中,多次调用以创建从System.in读取的任何对象将导致阻塞问题,一旦第一个对象打开了与Pipe或System.in,其他人不能。 I don't know why this exactly is for Birkner's code, but with the Pipe I think that it's because you can only open 1 stream to the object-ever. 我不知道为什么这正是Birkner的代码的原因,但是对于Pipe,我认为这是因为您永远只能向对象打开1个流。 Notice that if you call close on the first buffered reader, and then try to reopen System.in in your client code after having called it from the test, then the second attempt to open will fail because the pipe on the writer's side has been closed as well. 请注意,如果您在第一个缓冲的读取器上调用close,然后在从测试中调用它后尝试在客户端代码中重新打开System.in,则第二次尝试打开将失败,因为在编写器侧的管道已关闭也一样

Solution : Easy way to solve this, and probably not the best because it requires modifying the source of the actual project, but not in a horrendous way (yet). 解决方案 :解决此问题的简单方法,可能不是最好的方法,因为它需要修改实际项目的源代码,但还不需要以一种可怕的方式(到目前为止)。 So instead of having in the source of the actual project multiple BufferedReader creations, create a buffered reader, and pass the same reader reference around or make it a private variable of the class. 因此,不要在实际项目的源代码中创建多个BufferedReader,而是创建一个缓冲的读取器,然后将相同的读取器引用传递给该类,或使其成为类的私有变量。 Remember that if you have to declare it static that you should not initialize it in a static context because if you do, when the tests run, System.setIn will get called AFTER the reader has been initialized in your client. 请记住,如果必须将其声明为静态,则不应在静态上下文中对其进行初始化,因为如果这样做,则在运行测试时,将在读取器已在客户端中初始化之后调用System.setIn。 So it will poll on all readLine/whatever calls, just as it will if you try to create multiple objects from System.in. 因此,它将对所有readLine / whatever调用进行轮询,就像尝试从System.in中创建多个对象一样。 Notice that to have your reads segregated between calls from your reader, in this case BufferedReader, you can use newlines to segregate them in the original setup. 注意,要使您的读取在阅读器的调用之间分开,在本例中为BufferedReader,您可以使用换行符在原始设置中将它们分开。 This way, it returns what you want in each call in the client being tested. 这样,它将在被测试客户端的每个调用中返回您想要的内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM