0
votes

I am new to both ScalaMock and mocking in general. I am trying to test a method which calls a method in another (mocked) class and then calls a method on the returned object.

Detailed information:

So I am using ScalaTest and there are five classes involved in this test...

SubInstruction which I am testing

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)
}

Machine which must be mocked for the test

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 + ".")
  }
}

Registers which is required to construct a Machine object

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)
}

MockableMachine which I have created as the original Machine class does not have an empty constructor and therefore (as I understand) can not be mocked

class MockableMachine extends Machine(Labels(), Vector()){

}

and finally my test class SubInstructionTest which compiles but throws the exception below.

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)

  }

}

Throws:

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

-

I have been searching for a straightforward way to mock this class for hours, but have found nothing. For the time being I have settled on the workaround detailed below, but I was under the impression that mocking would offer a less convoluted solution to the problem of testing my SubInstruction class.

The workaround:

Delete the MockableMachine class and create a CustomMachine class which extends Machine and replaces the registers value with mockedRegisters provided at construction time.

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

a MockableRegisters class which I have created as the original does not have an empty constructor and therefore (as I understand) can not be mocked

class MockableRegisters extends Registers(32) {

}

and the SubInstructionTest class written in a slightly different way

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)

  }

}

As indicated, this feels like a workaround to me, is there not a simpler way to do this (perhaps similar to my original attempt)?

I have just included the essential code to ask the question, but you can find the full code on my GitHub account.

1

1 Answers

1
votes

I don't think mocking nested objects is supported by Scalamock implicitly. You'll have to mock the object returned by the first call which is what your working example does.

FWIW, Mockito supports this. Search for RETURNS_DEEP_STUBS.