简体   繁体   English

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

[英]Unit testing strategy in Scala with Try, Success, Failure

For the sake of simplicity suppose we have a method called listTail which is defined in the following way: 为了简单起见,假设我们有一个名为listTail的方法,该方法以以下方式定义:

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

Also we have a method which handles the exception when the list is empty. 我们还有一个方法可以在列表为空时处理异常。

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

Now I want to create a safe version of the listTail method, which uses both methods: 现在,我想创建一个安全版本的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)
 }
}

My question is, if the two private methods are already tested, then should I test the safe method as well? 我的问题是,如果两个私有方法都已经过测试,那么我也应该测试安全方法吗? And if yes, how? 如果是的话,怎么办? I was thinking just to check if the pattern matching cases are executed depending on the input. 我当时只是想检查是否根据输入内容执行了模式匹配案例。 That is, when we hit the Failure case then the handleEmptyList method is executed. 也就是说,当我们遇到“失败”情况时,将执行handleEmptyList方法。 But I am now aware of how to check this. 但是我现在知道如何检查这一点。

Or do I need to refactor my code, and put everything in a single method? 还是我需要重构代码,并将所有内容放在一个方法中? Even though maybe my private methods are much more complex than this in the example. 即使也许我的私有方法比示例中的复杂得多。

My test are written using ScalaTest. 我的测试是使用ScalaTest编写的。

Allowing your methods to throw intentionally is a bad idea and definitely isn't in the spirit of FP. 允许您的方法故意抛出是一个坏主意,并且绝对不符合FP的精神。 It's probably better to capture failure in the type signature of methods which have the ability to fail. 在具有失败能力的方法的类型签名中捕获失败可能更好。

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

Now your users know that this will return either an Success or a Failure and there's no magic stack unrolling. 现在,您的用户知道这将返回SuccessFailure并且没有魔术堆栈展开。 This already makes it easier to test that method. 这已经使测试该方法更加容易。

You can also get rid of the pattern matching with a simple def safeTailList(ls: List[Int]) = listTail(l).getOrElse(Nil) with this formulation -- pretty nice! 您也可以使用此公式使用简单的def safeTailList(ls: List[Int]) = listTail(l).getOrElse(Nil)摆脱模式匹配-很好!

If you want to test this, you can make it package private and test it accordingly. 如果要对此进行测试,可以将其打包,然后进行相应的测试。

The better idea would be to reconsider your algorithm. 更好的主意是重新考虑您的算法。 There's machinery that makes getting the safe tail built-in: 有使内置安全尾巴成为可能的机器:

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

It is actually the other way around: normally, you don't want to test private methods, only the public ones, because they are the ones that define your interactions with the outside world, as long as they work as promised, who cares what your private methods do, that's just implementation detail. 实际上,这是另一回事:通常,您不想测试私有方法,仅想测试公共方法,因为只要它们按承诺工作,它们便会定义您与外界的交互,他们会在乎什么?您的私有方法就可以了,这只是实现细节。

So, the bottom line is - just test your safeListTail , and that's it, no need to test the inner implementation separately. 因此,最重要的是-只需测试您的safeListTail ,仅此而已,无需分别测试内部实现。

BTW, you don't need the match there: Try(listTail(ls)).getOrElse(handleEmptyList(ls)) is equivalent to what you have there ... which is actually not a very good idea, because it swallows other exceptions, not just the one that is thrown when the list is empty, a better approach would be actually to reinstate match but get rid of Try : 顺便说一句,您不需要那里的matchTry(listTail(ls)).getOrElse(handleEmptyList(ls))等同于您在那里...实际上不是一个好主意,因为它吞没了其他异常,而不仅仅是在列表为空时抛出的那个,一种更好的方法实际上是恢复match但摆脱Try

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM