Scala测试与类型浓缩

Pen*_*gin 11 unit-testing dependency-injection scala mockito scalatest

例如,我已经变得依赖于类型丰富

object MyImplicits{
  implicit class RichInt(i: Int){
    def complexCalculation: Int = i * 200 
  }
}
Run Code Online (Sandbox Code Playgroud)

我在这样的代码中使用的

object Algorithm{
  def apply(rand: Random) = {
    import MyImplicits._
    rand.nextInt.complexCalculation + 1
  }
}
Run Code Online (Sandbox Code Playgroud)

但是我现在如何隔离和单元测试算法呢?特别是,我想嘲笑这样的实现complexCalculation:

class MyAlgorithmTest extends FreeSpec with MockitoSugar{
  import org.mockito.Mockito.when

  "MyApgorithm" {
    "Delegates complex calculation" in {
      val mockRandom = mock[Random]
      when(mockRandom.nextInt()).thenReturn(1)

      // This wouldn't work, but is the kind of thing I'm looking for
      //when(1.complexCalculation).thenReturn(2)
      val expected = 1 * 2 + 1

      val result = MyAlgorithm(mockRandom)
      assert(result === expected)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Tim*_*Tim 2

隐式启用组合,当您有组合时,通常不需要模拟,因为您可以用实现来替代测试。话虽这么说,在这种情况下,我不太喜欢隐式,只是看不到它们带来的价值。我会用老式的作文来解决它(正如我的其他评论中所暗示的):

trait Calculation {
  def calculation(i: Int): Int
}

trait ComplexCalculation extends Calculation {
  def calculation(i: Int): Int = i * 200
}

trait MyAlgorithm {
  self: Calculation =>

  def apply(rand: Random) = {
    calculation(rand.nextInt) + 1
  }
}

// somewehre in test package

trait MockCalculation extends Calculation {
  def calculation(i: Int): Int = i * 200
}

//test instance
object MyAlgorithm extends MyAlgorithm with MockCalculation
Run Code Online (Sandbox Code Playgroud)

如果你坚持使用隐式进行组合,你可以这样做:

trait Computation {
  def compute(i: Int): Int
}

object prod {
  implicit val comp = new Computation {
    def compute(i: Int): Int = i * 200
  }
}

object test {
  implicit val comp = new Computation {
    def compute(i: Int): Int = i + 2
  }
}

object Algorithm {
  def apply(rand: Random)(implicit comp: Computation) = {
    comp.compute(i) + 1
  }
}

// application site
import prod._

Algorithm(scala.util.Random) // will run * 200 computation

//test

import test._

Algorithm(scala.util.Random) // will run + 2 computation
Run Code Online (Sandbox Code Playgroud)

但这不会为您提供用于计算的点语法。我的直觉也反对这种方法,因为这是一种非常微妙的定义行为的方式,而且很容易在引入什么内容方面犯错误。