这个scala函数是否太重/做了太多事情而无法正确进行单元测试?
好的,我在看测试库,特别是ScalaTest和ScalaMock 我想编写一个测试来测试我编写的函数:这个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
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 =
???
对于您的“效果”,您可以使用ScalazIO
与控制台进行交互:
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 =
???
对于您的“效果”,您可以使用ScalazIO
与控制台进行交互:
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的东西非常有用,因为我以前没有用过,是时候开始实验了!