我已经加入了类型丰富,例如
object MyImplicits{
implicit class RichInt(i: Int){
def complexCalculation: Int = i * 200
}
}
我在像这样的代码中使用
object Algorithm{
def apply(rand: Random) = {
import MyImplicits._
rand.nextInt.complexCalculation + 1
}
}
但我现在如何隔离和单元测试算法呢?特别是,我想嘲笑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)
}
}
}
答案 0 :(得分:2)
Implicits启用合成,当你有合成时,通常不需要模拟,因为你可以用实际代替测试。话虽这么说,在这种情况下,我不是暗示的暗示,只是看不到他们带来的价值。我用旧学校的作文来解决它(正如我在其他评论中暗示的那样):
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
如果您坚持使用implicits进行合成,您可以这样做:
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
虽然这不会为您提供计算的点语法。我的直觉也违背了这种方法,因为这是一种非常微妙的定义行为的方式,很容易犯下导入的错误。
答案 1 :(得分:1)
RichInt.scala
trait RichInt {
def complexCalculation: Int
}
class RichIntImpl(i: Int) extends RichInt {
def complexCalculation = i * 200
}
Algorithm.scala
import scala.util.Random
class Algorithm(enrich: Int => RichInt) {
implicit val _enrich = enrich
def apply(rand: Random) = {
rand.nextInt.complexCalculation + 1
}
}
object Algorithm extends Algorithm(new RichIntImpl(_))
AlgorithmTest.scala
import org.scalatest.FreeSpec
import scala.util.Random
import org.mockito.Mockito._
class AlgorithmTest extends FreeSpec with MockSugar {
"MyApgorithm should" - {
"Delegate the complex calculation" in {
val mockRandom = mock[Random]
when(mockRandom.nextInt()) thenReturn 1
val algorithm = new Algorithm(
enrich = mocking[Int => RichInt] { enrich =>
when(enrich(1)).thenReturnMocking { richInt =>
when(richInt.complexCalculation).thenReturn(2)
}
}
)
val expected = 3
assert(algorithm(mockRandom) === expected)
}
}
}
MockSuger.scala
import org.scalatest.mockito.MockitoSugar
import org.mockito.stubbing.OngoingStubbing
// More sugars to make our tests look better.
trait MockSugar extends MockitoSugar {
def mocking[T <: AnyRef : Manifest](behavior: T => Unit): T = {
val m = mock[T]
behavior(m)
m
}
implicit class RichOngoingStubbing[T <: AnyRef : Manifest](stub: OngoingStubbing[T]) {
def thenReturnMocking(behavior: T => Unit) = {
val m = mock[T]
val s = stub.thenReturn(m)
behavior(m)
s
}
}
}
答案 2 :(得分:0)
以下使用scalatest api。模拟测试运行正常,隐式类转换正常。
// Implicit.scala in src/main/scala
package implicittesting
import scala.util.Random
object MyImplicits{
implicit class RichInt(i: Int){
def complexCalculation: Int = 200*i // make this complex :)
}
}
object Algorithm{
var current = 1
def apply(rand: Random) = {
import MyImplicits._
current = rand.nextInt
current.complexCalculation + 100
}
}
// ImplicitSuite.scala in src/main/test
package implicittesting
import org.scalatest.FunSuite
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
class DeleteSuite extends FunSuite {
import MyImplicits._
test("algorithm implicit class conversion test") {
assert(Algorithm(scala.util.Random) == Algorithm.current.complexCalculation + 200)
println(Algorithm.current)
}
}
答案 3 :(得分:0)
这是我提出的最好的。我愿意承认它似乎疯了。
import org.scalatest.FreeSpec
import org.scalatest.mockito.MockitoSugar
import scala.util.Random
trait MyImplicits {
implicit class RichInt(i: Int){
def complexCalculation: Int = complexCalculationImpl(i)
}
def complexCalculationImpl(i: Int) = i * 200
}
trait MyAlgorithm extends MyImplicits {
def apply(rand: Random) = {
rand.nextInt.complexCalculation + 1
}
}
//Implementation for use
object MyAlgorithm extends MyAlgorithm
class MyAlgorithmTest extends FreeSpec with MockitoSugar{
import org.mockito.Mockito.when
"MyApgorithm should" - {
"Delegate the complex calculation" in {
val mockRandom = mock[Random]
when(mockRandom.nextInt()).thenReturn(1)
val instance = new MyAlgorithm {
override def complexCalculationImpl(i: Int) = i * 2
}
val expected = 3 // Note we don't expect 201
assert(instance(mockRandom) === expected)
}
}
}