Scala中编写和单元测试的良好实践实用程序方法

时间:2019-02-18 10:15:22

标签: scala mockito scalatest

在特定情况下,您需要具有一些跨不同类所需的实用程序方法。为了解决这种情况,您创建了一个Util对象,在其中放置了所有这些方法

object AggregatorUtil {
  def aggregateValues(list : List[BigDecimal]) = //some logic...
}

// Import everything in the Utilities object
import AggregatorUtil._

,然后导入您的类中需要util的任何成员。但是,这样做的缺点是,因为您所有的方法都在单例对象内部,并且要模拟使用实用程序方法的类的对象和单元测试方法变得很棘手。

要再次解决此问题,想到的唯一解决方案是将功能提取为特征,然后模拟特征。

请让我知道是否还有其他方法可以处理和测试util方法,哪种方法更干净。

先谢谢了!

注意:-我在项目中使用了scalatest和mockito。

2 个答案:

答案 0 :(得分:1)

如果您需要进行模拟,那么将所有这些因素都模拟出来是一种前进的道路。如果没有必要进行嘲笑,则应避免嘲笑。不必要的模拟是不必要的。您只会浪费时间和精力在没有其他价值的东西上。

当您具有复杂的功能或您想将其视为black box的其他文件中的功能时,最好使用模拟功能,并假设它可以按预期工作(然后通常单独对这些内容进行单元测试)。如果您可以避免这种情况,并使用函数的 actual 功能,则可以更真实地了解应用程序的功能,并可以更快地发现新的错误/重大更改(如果您已嘲弄过)功能,而忘记更新您的模拟,您可能不会发现任何引入的新错误。

何时需要模拟的一个很好的例子是当您模拟对MVC应用程序(例如Scala Play微服务)中的数据库的调用时。显然,您在测试代码时显然不想运行实际的数据库,因此通常会模拟出连接器层并从连接器函数中返回虚拟/模拟数据。

您不会嘲笑的示例如下:

trait MyTrait {
  def toInt(str: String): Int
}

val mockedTrait = mock[MyTrait]
when(mockedTrait.toInt(eq("3")).thenReturn(3)

这是一个愚蠢的例子,但我认为它清楚地说明了这一点-这样做很荒谬。嘲笑并不总是答案。

答案 1 :(得分:0)

我主要使用Test-Implementation进行模拟,我发现它更具可读性,并且您不必学习模拟框架。

这里有个例子:

界面:

trait DataRepo {

  def persist(data: DataObject): Future[DataObject]

  def idents(): Future[List[String]]

  def insertData(dataCont: DataObject): Future[Int]
  ...
}

模拟界面:

object DataRepoMock extends DataRepo {

  def persist(data: DataObject): Future[DataObject] = ??? // only implement when needed

  def idents(): Future[List[String]] = Future.successful((0 to 10).map(_=>Random.nextInt(100)))

  def insertData(dataCont: DataObject): Future[Int] = Future.successful(Random.nextInt(100))
  ...
}

您还可以使用所有Scala好东西,例如模式匹配,以使您的模拟对输入的反应有所不同。

这是一个示例,它不仅仅由我自己使用;):  EPFLx: scala-reactiveX参见第2.5节“测试Actor系统”

  def fakeGetter(url:String, depth: Int):Props =
    Props(new Getter(url, depth){
      override def webClient: WebClient = FakeWebClient
    })