关于scala:使用ScalaMock在模拟类上测试嵌套方法调用

Test a nested method call on a mocked class using ScalaMock

我对ScalaMock和嘲讽都是陌生的。我试图测试一种方法,该方法在另一个(模拟的)类中调用一个方法,然后在返回的对象上调用一个方法。

详细资料:

所以我正在使用ScalaTest,此测试涉及五个类...

我正在测试的子指令

1
2
3
4
5
6
7
8
9
10
11
12
13
class SubInstruction(label: String, val result: Int, val op1: Int, val op2: Int) extends Instruction(label,"sub") {

  override def execute(m: Machine) {
    val value1 = m.regs(op1)
    val value2 = m.regs(op2)
    m.regs(result) = value1 - value2
  }
}

object SubInstruction {
  def apply(label: String, result: Int, op1: Int, op2: Int) =
    new SubInstruction(label, result, op1, op2)
}

必须为测试而模拟的机器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
case class Machine(labels: Labels, prog: Vector[Instruction]) {
  private final val NUMBEROFREGISTERS = 32
  val regs: Registers = new Registers(NUMBEROFREGISTERS)

  override def toString(): String = {
    prog.foldLeft("")(_ + _)
  }

  def execute(start: Int) =
    start.until(prog.length).foreach(x => prog(x) execute this)
}

object Machine extends App {
  if (args.length == 0) {
    println("Machine: args should be sml code file to execute")
  } else {
    println("SML interpreter - Scala version")

    val m = Translator(args(0)).readAndTranslate(new Machine(Labels(), Vector()))

    println("Here is the program; it has" + m.prog.size +" instructions.")
    println(m)
    println("Beginning program execution.")
    m.execute(0)
    println("Ending program execution.")
    println("Values of registers at program termination:")
    println(m.regs +".")
  }
}

注册构造Machine对象所需的寄存器

1
2
3
4
5
6
7
8
9
case class Registers(size: Int) {
  val registers: Array[Int] = new Array(size)

  override def toString(): String =
    registers.mkString("")

  def update(k: Int, v: Int) = registers(k) = v
  def apply(k: Int) = registers(k)
}

我作为原始Machine类创建的MockableMachine没有空的构造函数,因此(据我了解)不能被嘲笑

1
2
3
class MockableMachine extends Machine(Labels(), Vector()){

}

最后是我的测试类SubInstructionTest,该类可以编译但引发以下异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class SubInstructionTest extends FlatSpec with MockFactory with Matchers {

  val label1 ="f0"
  val result1 = 25
  val op1_1 = 24
  val op2_1 = 20
  val sub1 = SubInstruction(label1, result1, op1_1, op2_1)

 "A SubInstruction" should"retrieve the operands from the correct registers in the given machine" +
   "when execute(m: Machine) is called, and perform the operation saving the" +
   "result in the correct register." in {
    val mockMachine = mock[MockableMachine]

    inSequence {
      (mockMachine.regs.apply _).expects(op1_1).returning(50)
      (mockMachine.regs.apply _).expects(op2_1).returning(16)
      (mockMachine.regs.update _).expects(result1, 34)
    }
    sub1.execute(mockMachine)

  }

}

抛出:

java.lang.NoSuchMethodException:Registers.mock $ apply $ 0()

--

我一直在寻找一种简单的方法来模拟该类达数小时之久,但没有发现任何东西。暂时,我已经解决了以下详细说明的变通办法,但给人的印象是,模拟将为测试我的SubInstruction类的问题提供一个不太复杂的解决方案。

解决方法:

删除MockableMachine类,并创建一个CustomMachine类,该类扩展Machine并用构造时提供的模拟寄存器替换寄存器值。

1
2
3
4
class CustomMachine (mockedRegister: Registers) extends Machine(Labels(), Vector()) {
  override
  val regs: Registers = mockedRegister
}

我作为原始对象创建的MockableRegisters类没有空的构造函数,因此(据我了解)不能被嘲笑

1
2
3
class MockableRegisters extends Registers(32) {

}

和SubInstructionTest类以稍微不同的方式编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class SubInstructionTest extends FlatSpec with MockFactory with Matchers {

  val label1 ="f0"
  val result1 = 25
  val op1_1 = 24
  val op2_1 = 20
  val sub1 = SubInstruction(label1, result1, op1_1, op2_1)

 "A SubInstruction" should"retrieve the operands from the correct registers in the given machine" +
   "when execute(m: Machine) is called, and perform the operation saving the" +
   "result in the correct register." in {
    val mockRegisters = mock[MockableRegisters]
    val machine = new CustomMachine(mockRegisters)

    inSequence {
      (mockRegisters.apply _).expects(op1_1).returning(50)
      (mockRegisters.apply _).expects(op2_1).returning(16)
      (mockRegisters.update _).expects(result1, 34)
    }
    sub1.execute(machine)

  }

}

如前所述,这对我来说似乎是一种解决方法,难道没有一种更简单的方法(也许与我最初的尝试类似)吗?

我刚刚包含了问这个问题的基本代码,但是您可以在我的GitHub帐户上找到完整的代码。


我认为Scalamock并不支持模拟嵌套对象。 您必须模拟第一个调用返回的对象,这是您的工作示例所做的。

FWIW,Mockito支持这一点。 搜索RETURNS_DEEP_STUBS