Search code examples
scalaunit-testingmockito

mock-maker-inline causes test cases having traits with final methods to fail


We need to use mock-maker-inline to mock final classes of some third party library (e.g. Azure SDK).

We are using below versions of scalatest and mockito:

scalaVersion := "2.12.2"

val ScalaTestVersion              = "3.2.5"
val ScalaCheckVersion             = "1.14.2"
val MockitoVersion                = "3.4.0"
val DockerItVersion               = "0.9.9"
val MockJavaMailVersion           = "1.9"
val MockitoScalaVersion           = "1.1.4"
val ScalaPlusScalaCheckVersion    = "3.2.2.0"
val ScalaPlusMockitoVersion       = "3.2.10.0"


lazy val MockitoIssueSample = (project in file("."))
  .settings(
    name := "MockitoIssueSample",
    libraryDependencies += "org.scalatest" %% "scalatest" % ScalaTestVersion % Test,
    libraryDependencies += "org.scalacheck"                %% "scalacheck"               % ScalaCheckVersion % Test,
    libraryDependencies += "org.mockito"                   %  "mockito-core"             % MockitoVersion  % Test,
    libraryDependencies += "org.mockito"                   %% "mockito-scala"            % MockitoScalaVersion  % Test,
    libraryDependencies += "org.scalatestplus"             %% "scalacheck-1-14"          % ScalaPlusScalaCheckVersion  % Test,
    libraryDependencies += "org.scalatestplus"             %% "mockito-3-4"              % ScalaPlusMockitoVersion  % Test,
  )

After enabling mock-maker-inline in our Scala application, other test cases which are using trait having final methods are started to fail with below error:

[info] - should should invoke area of the appropriate shape *** FAILED ***
[info]   org.mockito.exceptions.misusing.UnnecessaryStubbingException: Unnecessary stubbings detected.
[info] Clean & maintainable test code requires zero unnecessary code.
[info] Following stubbings are unnecessary (click to navigate to relevant line of code):
[info]   1. -> at cortex.mockito.sample.AreaCalculatorSpec$$anon$1.<init>(AreaCalculatorSpec.scala:27)
[info] Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
[info]   at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
[info]   at scala.Option.fold(Option.scala:158)
[info]   at cortex.mockito.sample.AreaCalculatorSpec.withFixture(AreaCalculatorSpec.scala:13)
[info]   at org.scalatest.wordspec.AnyWordSpecLike.invokeWithFixture$1(AnyWordSpecLike.scala:1075)
[info]   at org.scalatest.wordspec.AnyWordSpecLike.$anonfun$runTest$1(AnyWordSpecLike.scala:1087)
[info]   at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
[info]   at org.scalatest.wordspec.AnyWordSpecLike.runTest(AnyWordSpecLike.scala:1087)
[info]   at org.scalatest.wordspec.AnyWordSpecLike.runTest$(AnyWordSpecLike.scala:1069)
[info]   at org.scalatest.wordspec.AnyWordSpec.runTest(AnyWordSpec.scala:1879)
[info]   at org.scalatest.wordspec.AnyWordSpecLike.$anonfun$runTests$1(AnyWordSpecLike.scala:1146)
[info]   ...

We have simulated this issue with test Scala application. If we disable mock-maker-inline then this test case works. Here, in this sample application we have added just single problematic test case.

Below is sample code:

  1. Shape.scala
    package mockito.sample
    
    trait Shape {

      final def printArea(): Unit = {
        println(s"Area is: $getArea()")
      }
    
      def getArea(): Double

    }
  1. Rectangle.scala
    package mockito.sample
    
    class Rectangle(l: Long, b: Long) extends Shape {

      override def getArea(): Double = {
        l * b
      }

    }
  1. AreaCalculator.scala
    package mockito.sample
    
    class AreaCalculator(shape: Shape) {

      def printArea(): Boolean = {
        shape.printArea()
        true
      }

    }
  1. AreaCalculatorSpec.scala
    package mockito.sample
    
    import org.mockito.integrations.scalatest.IdiomaticMockitoFixture
    
    import org.scalatest.concurrent.ScalaFutures
    import org.scalatest.matchers.must.Matchers.convertToAnyMustWrapper
    import org.scalatest.matchers.should.Matchers
    
    import org.scalatest.wordspec.AnyWordSpec
    import org.scalatest.{EitherValues, TryValues}
    import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
    
    class AreaCalculatorSpec extends AnyWordSpec
      with Matchers
      with ScalaFutures
      with EitherValues
      with TryValues
      with IdiomaticMockitoFixture
      with ScalaCheckPropertyChecks {

      trait Setup {
        val rectangle = mock[Rectangle]
        val areaCalculator = new AreaCalculator(rectangle)
      }
    
      "AreaCalculator#printArea" should {
        "should invoke area of the appropriate shape" in new Setup {
          rectangle.getArea() shouldReturn 40.0
          areaCalculator.printArea() mustBe true
        }
      }

    }

Please check and suggest your valuable inputs. Let me know if any other details are required.

Thank you,

Rakesh Dhandhukiya


Solution

  • How are you disabling the mock-maker-inline?

    If you can update mockito and scalatest to the latest versions, this should go away: I tried reproducing the error but the test passes in my case.

    It appears IdiomaticMockitoFixture is deprecated and you should use IdiomaticMockito.

    However, I do get this warning when adding mock-maker-inline:

    OpenJDK 64-Bit Server VM warning: Sharing is only supported for 
      boot loader classes because bootstrap classpath has been appended
    

    It seems to be harmless though. I tried disabling my Instrumenting agent, but I still get it. (Removing mock-maker-inline makes the warning disappear)

    Here's the code, a bit modified:

    trait Shape {
      final def printArea(): Unit = println(s"Area is: $getArea")
      def getArea: Double
    }
    
    class Rectangle(l: Long, b: Long) extends Shape {
      override def getArea: Double = l * b
    }
    
    class AreaCalculator(shape: Shape) {
      def printArea(): Boolean = {
        shape.printArea()
        true
      }
    }
    

    AreaCalculatorSpec updated with the new dependencies:

    import org.mockito.IdiomaticMockito
    import org.scalatest.concurrent.ScalaFutures
    import org.scalatest.matchers.must.Matchers.convertToAnyMustWrapper
    import org.scalatest.matchers.should.Matchers
    import org.scalatest.wordspec.AnyWordSpec
    import org.scalatest.{EitherValues, TryValues}
    import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
    
    class AreaCalculatorSpec
        extends AnyWordSpec
        with Matchers
        with ScalaFutures
        with EitherValues
        with TryValues
        with IdiomaticMockito
        with ScalaCheckPropertyChecks {
    
      trait Setup {
        val rectangle: Rectangle = mock[Rectangle]
        val areaCalculator = new AreaCalculator(rectangle)
      }
    
      "AreaCalculator#printArea" should {
        "should invoke area of the appropriate shape" in new Setup {
          rectangle.getArea shouldReturn 40.0
          areaCalculator.printArea() mustBe true
        }
      }
    }
    

    Setup for my mock-maker-inline:

    enter image description here

    My build.sbt:

    libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.12" % Test
    libraryDependencies += "org.scalamock" %% "scalamock" % "5.2.0" % 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