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

Mocking InputStreamReader with console input in 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. A Writerflush()ed

这是我目前的情况:

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)
    }
}

如您所见,在给定 "any" 输入的情况下,我很难将模拟 CommandFactory 连接到 return 模拟 Command。我也很难显式地向模拟 readerInpuStream 发送换行符(以触发场景)。我也不确定测试方法是否强制排序(首先执行命令,然后刷新writer)。

关于我哪里出错了有什么想法吗?

您应该使用 >> 运算符而不是 <<。此外,在 Spock 中,any 是 _ 字符。

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

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

你的 MyReader 应该被模拟为 return 另一个模拟的 InputStream :

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

最后,要订购交互,您应该使用多个 'then' 块:

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

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