这个 Scala 函数是否 heavy/doing 太多东西无法正确进行单元测试?

Is this scala function to heavy/doing too many things to unit test properly?

好的,所以我正在查看测试库,特别是 ScalaTest 和 ScalaMock。

我想写一个测试来测试我写的这个功能:

def gameMenuSelect(): State = {
  Try(UI.readOption) match {
    case Success(i) => {
      i match {
        case 1 => HumanGame
        case 2 => MachineGame
        case 3 => sys.exit(0)
        case _ =>
          UI.invalidSelectionMsg
          ChoosingGame
      }
    }
    case Failure(e) => UI.invalidSelectionMsg; ChoosingGame
  }

}

有点背景,UI.readOption 很简单scala.io.StdIn.readIntState 是一个特征 - 随后 HumanGameMachineGameChoosingGame 也是扩展 State.

的特征

问题是我不知道如何测试它,原因是我觉得这个功能做的太多了。

它正在读取输入,验证给定的输入确实是 number/integer 而不是抛出 NumberFormatException。鉴于输入是一个整数,它与允许的整数匹配。

我真的觉得有很多东西要测试,还有很多我不确定是否可以单元测试。

请问大家是不是觉得这个函数做的事情太多了,要不要尝试把整型的读取和整型的匹配打散?

谢谢。

在我看来,你的函数有太多的副作用。不仅是读整数,还有sys.exit(0)。您可以更改方法以接受整数作为参数,并添加可用于案例 3 的 EndingGame 状态。然后您将拥有一个易于测试的纯函数。

是的,绝对应该尝试将 "side-effecting" 位、读取和写入与选择逻辑分开。选择逻辑可以return类似于

import scalaz._, Scalaz._

def selectGame(i: Int): GameError \/ State = 
  i match {
    case 1 => HumanGame.right
    case 2 => MachineGame.right
    case _ => InvalidGame(i).left
  }

sealed trait GameError
case class InvalidGame(i: Int) extends GameError

object GameError {
  def render(e: GameError): String =
    e match {
      case InvalidGame(i) => 
        s"Invalid game choice: $i. Only 1 and 2 are acceptable values"
    }
}

请注意,我还将错误建模为特定类型,而不仅仅是使用字符串。

然后你可以对你的号码解析做同样的事情:

def parseInt(i: String): ParseError \/ Int = 
  ???

对于您的 "effects",您可以使用 Scalaz IO 与控制台进行交互:

def readLine: IO[String] = 
  IO(StdIn.readLine)

def printLine(line: String): IO[Unit] = 
  IO(println(line))

然后,使用更多代码,您可以使用 EitherT[IO, E, A] monad 来 "assemble" 您的所有函数:

 // I will provide a full example if you want to go this way
 val actions: EitherT[IO, ApplicationError, Unit] = 
   for {
     line <- readLine
     i    <- parseInt(line)
     s    <- selectGame(i)  
     _    <- printLine(s.render)
   } yield ()

actions 值将具有 IO 副作用并收集错误,如果有错误则停止进程。

最终,这一切都会让您的测试变得更容易,因为您已经隔离了更容易测试的 "pure" 部分:没有设置,没有模拟,只有纯函数。