使用尝试,成功,失败的Scala中的单元测试策略

时间:2018-11-07 12:56:04

标签: scala unit-testing scalatest

为简单起见,假设我们有一个称为listTail的方法,该方法的定义如下:

private def listTail(ls: List[Int]): List[Int] = {
 ls.tail
}

我们还有一个方法可以在列表为空时处理异常。

private def handleEmptyList(ls: List[Int]): List[Int] = {
 if(ls.isEmpty) List.empty[Int]
}

现在,我想创建一种安全版本的listTail方法,该方法同时使用两种方法:

import scala.util.{Try, Success, Failure}

def safeListTail(ls: List[Int]): List[Int] = {
 val tryTail: Try[List[Int]] = Try(listTail(ls))
 tryTail match {
   case Success(list) => list
   case Failure(_) => handleEmptyList(ls)
 }
}

我的问题是,如果两个私有方法都已经过测试,那么我也应该测试安全方法吗?如果是的话,怎么办? 我当时只是想检查是否根据输入内容执行了模式匹配案例。也就是说,当我们遇到“失败”情况时,将执行handleEmptyList方法。但是我现在知道如何检查这一点。

还是我需要重构代码,并将所有内容放在一个方法中?即使也许我的私有方法比示例中的复杂得多。

我的测试是使用ScalaTest编写的。

2 个答案:

答案 0 :(得分:2)

让您的方法故意抛出是一个坏主意,而且绝对不符合FP的精神。在具有失败能力的方法的类型签名中捕获失败可能更好。

private def listTail(ls: List[Int]): Try[List[Int]] = Try {
  ls.tail
}

现在您的用户知道这将返回SuccessFailure,并且没有魔术堆栈展开。这样已经可以更轻松地测试该方法。

您还可以通过这种公式摆脱使用简单的def safeTailList(ls: List[Int]) = listTail(l).getOrElse(Nil)进行模式匹配的过程–相当不错!

如果要对此进行测试,可以将其打包并进行相应的测试。

更好的主意是重新考虑您的算法。有内置安全尾巴的机械:

def safeTailList(ls: List[Int]) = ls.drop(1)

答案 1 :(得分:0)

实际上是相反的情况:通常,您不希望测试私有方法,而只是测试公共方法,因为只要它们按承诺工作,它们就是定义您与外界交互的方法,谁在乎您的私有方法做什么,那只是实现细节。

因此,最重要的是-仅测试您的safeListTail,仅此而已,无需分别测试内部实现。

顺便说一句,您不需要那里的matchTry(listTail(ls)).getOrElse(handleEmptyList(ls))等同于那里的东西……这实际上不是一个好主意,因为它吞没了其他异常,而不仅仅是当列表为空时抛出的一个更好的方法实际上是恢复match但摆脱Try

  ls match {
     case Nil => handleEmptyList(ls)
     case _ => listTail(ls)
  }