有没有办法在 Scala 中模拟 Singleton 对象
Is there any way to mock Singleton object in Scala
我有以下代码:
trait Calculator {
def add(x:Int, y:Int):Int
def multiply(x:Int,y: Int):Int
}
trait MyCalculator extends Calculator {
override def add(x: Int, y: Int): Int = x+y //in real live it calls remote service which is not avaialble in test
override def multiply(x: Int, y: Int): Int = x*y //in real live it calls remote service which is not avaialble in test
}
object MyCalculator extends MyCalculator
现在我有计算器服务了:
trait CalculatorServiceTrait {
def calculate(x:Int,sign:String,y:Int):Int
}
trait CalculatorService extends CalculatorServiceTrait{
override def calculate(x: Int, sign: String, y: Int): Int = {
sign match{
case "+" => MyCalculator.add(x,y)
case "*" => MyCalculator.multiply(x,y)
case _ => 0
}
}
}
object CalculatorService extends CalculatorService
现在我想使用 Mockito 模拟 MyCalculator 给我带来不正确的结果。
"Calculator Service" should{
"return 0 when 2 and 2 used " in{
val MyCalculatorMock = mock[MyCalculator]
when(MyCalculatorMock.multiply(2,2)).thenReturn(0)
class CalculatorServiceUnderTest extends CalculatorService with MyCalculator
new CalculatorServiceUnderTest with MyCalculator
val c = new CalculatorServiceUnderTest
val result = c.calculate(2,"+",2)
result shouldEqual(0)
}
}
但我仍然得到“4”而不是“0”
有没有办法处理这样的测试用例?
P.S: 我可以更改一些 class 或特征实现,但进行全局重构可能会有问题
嗯,你的模拟对象没有在任何地方使用,所以,它永远不会被调用并不奇怪,不是吗?
要回答你的问题,不,你不能模拟单例,这就是为什么像这样直接使用它几乎不是一个好主意。需要从外部提供组件的外部依赖关系,以便组件可以独立测试。
做你想做的事情的一种方法是使 CalculatorService
成为 class 并将 MyCalculator
实例作为参数传递给构造函数:
class CalculatorService(calc: MyCalculator = MyCalculator)
extends CalculatorServiceTrait {
override def calculate(x: Int, sign: String, y: Int): Int = sign match {
case "+" => calc.add(x,y)
case "*" => calc.multiply(x,y)
case _ => 0
}
}
}
然后,在你的测试中,你可以这样做:
val testMe = new CalculatorService(mock[MyCalculator])
如果由于某种原因必须保留特征,您可以使用 "cake pattern" 提供外部依赖项:
trait CalculatorProvider {
def calc: MyCalculator
}
trait CalculatorService { self: CalculatorProvider =>
...
}
object CalculatorService extends CalculatorService with CalculatorProvider {
def calc = MyCalculator
}
val testMe = new CalculatorService with CalculatorProvider {
val calc = mock[MyCalculator]
}
我有以下代码:
trait Calculator {
def add(x:Int, y:Int):Int
def multiply(x:Int,y: Int):Int
}
trait MyCalculator extends Calculator {
override def add(x: Int, y: Int): Int = x+y //in real live it calls remote service which is not avaialble in test
override def multiply(x: Int, y: Int): Int = x*y //in real live it calls remote service which is not avaialble in test
}
object MyCalculator extends MyCalculator
现在我有计算器服务了:
trait CalculatorServiceTrait {
def calculate(x:Int,sign:String,y:Int):Int
}
trait CalculatorService extends CalculatorServiceTrait{
override def calculate(x: Int, sign: String, y: Int): Int = {
sign match{
case "+" => MyCalculator.add(x,y)
case "*" => MyCalculator.multiply(x,y)
case _ => 0
}
}
}
object CalculatorService extends CalculatorService
现在我想使用 Mockito 模拟 MyCalculator 给我带来不正确的结果。
"Calculator Service" should{
"return 0 when 2 and 2 used " in{
val MyCalculatorMock = mock[MyCalculator]
when(MyCalculatorMock.multiply(2,2)).thenReturn(0)
class CalculatorServiceUnderTest extends CalculatorService with MyCalculator
new CalculatorServiceUnderTest with MyCalculator
val c = new CalculatorServiceUnderTest
val result = c.calculate(2,"+",2)
result shouldEqual(0)
}
}
但我仍然得到“4”而不是“0”
有没有办法处理这样的测试用例?
P.S: 我可以更改一些 class 或特征实现,但进行全局重构可能会有问题
嗯,你的模拟对象没有在任何地方使用,所以,它永远不会被调用并不奇怪,不是吗?
要回答你的问题,不,你不能模拟单例,这就是为什么像这样直接使用它几乎不是一个好主意。需要从外部提供组件的外部依赖关系,以便组件可以独立测试。
做你想做的事情的一种方法是使 CalculatorService
成为 class 并将 MyCalculator
实例作为参数传递给构造函数:
class CalculatorService(calc: MyCalculator = MyCalculator)
extends CalculatorServiceTrait {
override def calculate(x: Int, sign: String, y: Int): Int = sign match {
case "+" => calc.add(x,y)
case "*" => calc.multiply(x,y)
case _ => 0
}
}
}
然后,在你的测试中,你可以这样做:
val testMe = new CalculatorService(mock[MyCalculator])
如果由于某种原因必须保留特征,您可以使用 "cake pattern" 提供外部依赖项:
trait CalculatorProvider {
def calc: MyCalculator
}
trait CalculatorService { self: CalculatorProvider =>
...
}
object CalculatorService extends CalculatorService with CalculatorProvider {
def calc = MyCalculator
}
val testMe = new CalculatorService with CalculatorProvider {
val calc = mock[MyCalculator]
}