这个scala函数是否太重/做了太多事情而无法正确进行单元测试?

这个scala函数是否太重/做了太多事情而无法正确进行单元测试?,scala,scalatest,Scala,Scalatest,好的,我在看测试库,特别是ScalaTest和ScalaMock 我想编写一个测试来测试我编写的函数: def gameMenuSelect(): State = { Try(UI.readOption) match { case Success(i) => { i match { case 1 => HumanGame case 2 => MachineGame case 3 => sys.exit

好的,我在看测试库,特别是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.readInt
状态
是一种特性-随后
人类游戏
机器游戏
选择游戏
也是扩展
状态
的特性

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

它正在读取输入,验证给定的输入是否确实是一个数字/整数,并且不会抛出
NumberFormatException
。假设输入是一个整数,它与允许的整数匹配

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

你是否觉得这个函数做了太多的事情,我是否应该尝试打破整数的读取和匹配,我可以有一些意见吗


谢谢

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

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

是的,应该绝对尝试从选择逻辑中分离“副作用”位(读写)。选择逻辑可以返回如下内容

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 = 
  ???
对于您的“效果”,您可以使用Scalaz
IO
与控制台进行交互:

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

def printLine(line: String): IO[Unit] = 
  IO(println(line))
然后,再编写一些代码,就可以使用
EitherT[IO,E,a]
monad来“组装”所有函数:

 // 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 ()
//如果您想这样做,我将提供一个完整的示例
val操作:EitherT[IO,应用程序错误,单位]=
为了{

行是的,应该绝对尝试从选择逻辑中分离“副作用”位,读和写

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 = 
  ???
对于您的“效果”,您可以使用Scalaz
IO
与控制台进行交互:

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

def printLine(line: String): IO[Unit] = 
  IO(println(line))
然后,再编写一些代码,就可以使用
EitherT[IO,E,a]
monad来“组装”所有函数:

 // 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 ()
//如果您想这样做,我将提供一个完整的示例
val操作:EitherT[IO,应用程序错误,单位]=
为了{

行此函数做得太多了!因为它显然没有使用依赖注入,您可以模拟尝试和强制覆盖成功和失败。此函数做得太多了!因为它显然没有使用依赖注入,您可以模拟尝试和强制覆盖成功和失败。这是一个巨大的输入,最初是函数on只取一个整数并返回一个值,但我随后将其归为一个,认为这是一个合乎逻辑的操作,但这使它变得更复杂,ScalaZ的东西非常有用,因为我以前没有使用过,是时候开始实验了!谢谢Eric,这是一个巨大的输入,最初函数只取一个整数然后返回一个值,但我把它归为一个,认为这是一个合乎逻辑的事情,但它使它变得更复杂,ScalaZ的东西非常有用,因为我以前没有用过,是时候开始实验了!