java - 创建用户提示并模拟交互

标签 java scala

我有以下代码(为了问题的目的我对其进行了简化):

  def openFile(directory: File): Try[String] = {
      var input = ""
      do {
        input = readLine("prompt>")
        println("alibaba.txt: 100%")
      } while(input != ":quit")
   }

工作流程是这样的:

用户收到提示:

提示>

用户输入alibaba,然后按回车

用户看到:

alibaba.txt: 100%
prompt>

一切:

prompt>alibaba
alibaba.txt: 100%
prompt>

现在,我想测试一下。

我编写了以下代码来测试用户交互:

  test("simulate user input from readline") {
    val prompt = new Prompt()
    prompt.openFile(new File("../resources"))

    val in = new ByteArrayInputStream("alibaba\n".getBytes)
    System.setIn(in)

    val scanner: Scanner = new Scanner(System.in)
    val programResponse: String = scanner.nextLine()
    println("programResponse: " + programResponse)

    System.setIn(System.in)
    assert(programResponse == "alibaba.txt: 100%")
  }

但是,我得到这个结果,但我很困惑:

"alibaba[]" did not equal "alibaba[.txt: 100%]"

那么如何让测试模拟用户交互呢?

如何读取我的程序所写的内容?

最佳答案

在我看来,你应该以一种易于测试的方式构建你的代码,这意味着你应该将 IO 提取到更高的抽象。

出于演示目的,我将您的示例稍微修改为以下代码:

import java.util.Scanner

object YourObject {

  def consoleMethod(in: () => String = new Scanner(System.in).nextLine,
                    out: String => Unit = System.out.println): Unit = {
    var input = ""
    do {
      out("prompt>")
      input = in()
      out("alibaba.txt: 100%")
    } while (input != ":quit")
  }
}

让我们分解一下:

  • in: () => String = new Scanner(System.in).nextLine 代表我们的用户输入源。默认情况下它是System.in
  • out: String => Unit = System.out.println 代表我们的输出源。默认情况下它是System.out

让我们测试一下用户立即输入 ":quit" 时的场景:

import org.scalatest.{Matchers, WordSpec}

class Test extends WordSpec with Matchers {

  "We" should {
    "simulate user input from readline" in {
      var outputs = List.empty[String]
      def accumulate(output: String): Unit = outputs = outputs :+ output

      val in: () => String = () => ":quit"
      val out: String => Unit = accumulate _

      YourObject.consoleMethod(in, out)

      outputs shouldBe List("prompt>", "alibaba.txt: 100%")
    }
  }
}
<小时/>

如果您想要更多控制,可以使用scalamock:

在这种情况下,我们可以模拟 inout 以按照我们需要的方式进行操作。

val in = mock[() => String]
val out = mock[String => Unit]

设置来源期望:

(in.apply _).expects().anyNumberOfTimes().onCall(_ => ":quit")

现在我们要设置out来记录我们要写的内容:

var outputs = List.empty[String]
def accumulate(output: String): Unit = outputs = outputs :+ output

(out.apply _)
  .expects(new FunctionAdapter1[String, Boolean](_ => true))
  .anyNumberOfTimes()
  .onCall(accumulate _)

完美,现在让我们设置期望:

outputs shouldBe List("prompt>", "alibaba.txt: 100%")

测试的完整源代码:

import org.scalamock.function.FunctionAdapter1
import org.scalamock.scalatest.MockFactory
import org.scalatest.{Matchers, WordSpec}

class Test extends WordSpec with Matchers with MockFactory {

  "We" should {
    "simulate user input from readline" in {
      val in = mock[() => String]
      val out = mock[String => Unit]

      (in.apply _).expects().anyNumberOfTimes().onCall(_ => ":quit")

      var outputs = List.empty[String]

      def accumulate(output: String): Unit = outputs = outputs :+ output

      (out.apply _)
        .expects(new FunctionAdapter1[String, Boolean](_ => true))
        .anyNumberOfTimes()
        .onCall(accumulate _)

      YourObject.consoleMethod(in, out)

      outputs shouldBe List("prompt>", "alibaba.txt: 100%")
    }
  }
}

关于java - 创建用户提示并模拟交互,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40344117/

相关文章:

scala - 为什么 scalac 会在这里出现 "diverging implicit expansion"错误?

Java:FileReader 和 FileWriter 不能一起工作

java - 我无法使用 Java 代码接收网站的所有 HTML 内容

scala - 在 Dataproc 上运行 Spark + Scala + Jupyter

scala - 什么时候在 Scala 中使用 SBT 和 Ivy 模棱两可地选择具有相同定义和类路径的两个类?

scala - 在 Spark Structured Streaming 中将数据内部连接到左连接 DataFrame 时丢失条目

Scala:从一种类型的集合到另一种类型的 yield

java - 使用一个 war 文件部署两个 glassfish 应用程序,在不同的数据库上运行

java - 不使用 Hibernate 创建表

java - 实例化 java.io.File 以从类路径读取资源