创建用户提示并模拟交互

时间:2016-10-31 14:23:11

标签: java scala

我有以下代码(我为了问题而简化了代码):

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

工作流程如下:

用户收到提示:

prompt>

用户写alibaba然后按Enter键

用户看到:

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%]"

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

我如何阅读我的程序写的内容?

1 个答案:

答案 0 :(得分:1)

IMO你应该以一种简单可测试的方式构建你的代码,这意味着你应该将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%")
    }
  }
}