在Spock中使用控制台输入模拟InputStreamReader

时间:2016-01-25 12:54:38

标签: unit-testing groovy mocking spock

我有以下代码:

class MyWorker implements Runnable {
    MyReader reader
    Writer writer // java.io.Writer
    CommandFactory commandFactory

    @Inject
    MyWorker(MyReader reader, Writer writer, CommandFactory commandFactory) {
        super()
        this.reader = reader
        this.writer = writer
        this.commandFactory = commandFactory
    }

    @Override
    void run() {
        try {
            String line
            while ((line = reader.readLine()) != null) {
                // Commands have an execute method.
                commandFactory.createCommand(line).execute(writer)
                writer.flush()
            }
        } catch(InterruptedException ex) {
            log.warn("${this} interrupted with: ${ExceptionUtils.getStackTrace(ex)}")
        }
    }
}

我正在尝试编写一个Spock Specification来验证按下换行符(Enter键)时发生的一些事情:

  1. 执行command
  2. Writerflush() ed
  3. 这是我到目前为止所拥有的:

    class MyWorkerSpec extends Specification {
        def "when enter key is pressed then a command is selected and executed and the writer is flushed"() {
            given: "a running fixture with some mock dependencies"
            MyReader reader = Mock()
            Writer writer = Mock()
            Command command = Mock()
            CommandFactory commandFactory = Mock()
            commandFactory.createCommand(Spock.ANY) << command      // FIXME #1: Have it always return mock cmd
    
            MyWorker worker = new MyWorker(reader, writer, commandSelector)
            worker.run()
    
            when: "the enter key is pressed"
            // reader.input gives you a java.io.InputStream
            reader.input.write << '\n'                              // FIXME #2: Send it a newline
    
            then: "a command is executed"
            1 * command.execute(Spock.ANY)                          // FIXME #4: How to specify "any"
    
            and: "a writer is flushed"
            1 * writer.flush()                                      // FIXME #5 (how to guarante ordering of cmd -> flush)
        }
    }
    

    正如您所看到的,我很难将模拟CommandFactory连接起来,以返回给出“任意”输入的模拟Command。我也很难明确地发送模拟reader的{​​{1}}换行符(以便触发场景)。我也不确定测试方法是否强制执行排序(首先执行命令,然后刷新InpuStream)。

    关于我哪里出错的任何想法?

1 个答案:

答案 0 :(得分:2)

您应该使用>>运算符代替<<。而且,任何在Spock中都是_字符。

要创建始终返回命令的模拟,请使用:

def commandFactory = Mock(CommandFactory) {
  createCommand(_) >> command
}

你的MyReader应该被模拟以返回另一个模拟的InputStream:

def readerStream = Mock(InputStream) {
  write() >> '\n'
}
def myReader = Mock(MyReader) {
  input >> readerStream
}

最后,要订购互动,您应该使用多个&#39;然后&#39;阻止:

then: "a command is executed"
  1 * command.execute(_)

then: "a writer is flushed"
  1 * writer.flush()