Home > Blockchain >  Mock case class in scala
Mock case class in scala

Time:07-24

I have a trait

 trait Writer[T <: Attribute] {
  def read(boundingBox: BoundingBox): Future[List[T]]
}

and a case class

case class WriterImpl[T <: Attribute]()(implicit manifest: Manifest[T]) extends Writer[T] with LazyLogging {

override def read(boundingBox: BoundingBox): Future[List[T]] = {
}
..
}

In the Test class I want to mock the method read()

I tried this with exact values

  val writer = mock[WriterImpl[Attrib]]
        when(writer.read(new BoundingBox(41.90178412, 41.8798685, -87.62687021, -87.64884287)))
          .thenReturn(Future.successful(scala.collection.immutable.List(list)))

 verify(writer).read(new BoundingBox(41.90178412, 41.8798685, -87.62687021, -87.64884287))

also tried

val writer = mock[WriterImpl[Attrib]]
            when(writer.read(ArgumentMatchers.any()))
              .thenReturn(Future.successful(scala.collection.immutable.List(list)))
    
     verify(writer).read(ArgumentMatchers.any())

Instead of calling the mock method it calls the actual method

CodePudding user response:

In the first example, the problem is you use 2 different BoundingBox objects as parameters for the read method, one for the when method, and one for the verify method. They will not match because at runtime they are different instances of the same BoundingBox class. You should use the same object. Store it before the when clause and then call verify passing that same object.

I assume you just removed the obvious call to writer.read(...) anywhere between the when and verify clauses. verify expects at least one call to read, otherwise it throws a "Wanted but not invoked: writerImpl.read(...); Actually, there were zero interactions with this mock.."

The second example should work, but you will still have the same issue if you'll try to check the result. You will result in 2 different instances of a Future: once in the when and once in the verify. Again, at runtime, they are 2 different objects, so they will not match if you use :

result shouldBe Future.successful(scala.collection.immutable.List(new Attribute))

The output confirms the runtime sees 2 different instances:

Expected :Future(Success(List(org.scala.snippets.Test$Attribute@373052b5)))
Actual   :Future(Success(List(org.scala.snippets.Test$Attribute@17207f5b)))

Again, you should store the Future before when and pass the same Future to both when and shouldBe. With these adjustments this should work. Now Checking the result of read confirms the same Future was returned from the call:

import Test.{Attribute, BoundingBox, WriterImpl}

import org.mockito.MockitoSugar
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

import scala.concurrent.Future

class ProgramTest extends AnyWordSpec with MockitoSugar with Matchers {
  "Program " should {
    "return the mocked succf " in {
      val succf  = Future.successful(scala.collection.immutable.List(new Attribute))
      val bb     = new BoundingBox(41.90178412, 41.8798685, -87.62687021, -87.64884287)
      val writer = mock[WriterImpl[Attribute]]
      when(writer.read(bb)).thenReturn(succf)
    
      val result = writer.read(bb)
    
      verify(writer, times(1)).read(bb)
      result shouldBe succf
    }
  }
}

Here's my build.sbt in case you need it:

libraryDependencies  = "org.scalatest" %% "scalatest" % "3.2.12" % Test
libraryDependencies  = "org.scalatestplus" %% "scalacheck-1-16" % "3.2.12.0" % Test
libraryDependencies  = "org.scalatestplus" %% "mockito-4-5" % "3.2.12.0" % Test
libraryDependencies  = "org.mockito" % "mockito-core" % "4.6.1" % Test
libraryDependencies  = "org.mockito" %% "mockito-scala" % "1.17.7" % Test
  • Related