mockito-scala icon indicating copy to clipboard operation
mockito-scala copied to clipboard

Add support for Scala 3

Open ultrasecreth opened this issue 3 years ago • 17 comments

Scala 3 has a ton of changes on the internals and more importantly, the macros. mockito-scala makes heavy use of macros, reflection and it also relies on knowing how certain features are compiled/implemented so they can run in the JVM.

This means that pretty much a new lib has to be written for dotty.

My current line of thinking is that we need to:

  1. Implement the basic features for dotty in a separate branch/repo
  2. Dive in with the more complex ones
  3. Split out the public API from the existing project as a separate module
  4. Make the existent lib and the new one use and implement the new public API, so people can use them interchangeably and migrations are easier.
  5. Profit(?) :)

ultrasecreth avatar Feb 24 '21 10:02 ultrasecreth

This replaces #330

ultrasecreth avatar Feb 24 '21 10:02 ultrasecreth

Scala 3 first official version got released yesterday: https://github.com/lampepfl/dotty/releases/tag/3.0.0 :)

tOverney avatar May 14 '21 09:05 tOverney

Yeah, my main blocker is the tooling, I really need good debugging tools to reverse engineer what mockito and/or the scala compiler does and I found that almost impossible at the moment. This is almost blocked I'd say until IntelliJ or any other real IDE has proper support for Scala 3 :(

ultrasecreth avatar May 14 '21 16:05 ultrasecreth

Hi @bbonanno! We at Twitter are actively trying to cross-build our Util library (https://github.com/twitter/util) with Scala 3. We use mockito-scala in a base subproject that many others depend on. We've identified that we depend on ArgumentMatchersSugar and IdiomaticMockito in mockito-scala and depend on much more in mockito-scala-scalatest. Do you have an estimate for when we might be able to expect support for Scala 3?

In the meantime, I'm looking at potentially using 2.13-specific versions for mockito-scala and mockito-scala-scalatest for the Scala 3 crossbuild, but I'm still not sure how that will pan out. I will comment when I know more.

cc @NataliaMGonzalez @hamdiallam

dotordogh avatar Jul 05 '21 21:07 dotordogh

Hi @dotordogh, Sadly the sorry state of affairs on the Scala 3 tooling is blocking me on the development of the Scala 3 version, last 2 times I tried I hit the hard wall of not having a fully functional IDE, that allows me to debug and reverse engineer what the Scala 3 compiler does. At the time the consensus was to use print lines to "debug", but that doesn't cut it for me as I need to see how the Scala constructs reach the Java core of Mockito, which is key for me to bridge the gap and make everything work. I plan to have another go probably by the end of the month, but if the tooling is still in the same sorry state I'm not sure if I'll manage to do anything meaningful.

If you, and anybody else that's looking to migrate, can outline the features that are deemed the most important, I can try to focus on those first and hopefully release a "lite" version whilst the bulk of the features are still being developed.

I'd also be very interested to see how did it go using the 2.13 version for the cross-build :)

Cheers!

ultrasecreth avatar Jul 06 '21 08:07 ultrasecreth

@bbonanno Got it! Thank you for responding! I did see you mention that the IDE support for Scala 3 was subpar, but I was mainly wondering if things had improved since then.

I did try it with the 2.13 version and it seems to work fine for the cross-build, so we'll stick with that until we can upgrade.

@NataliaMGonzalez or I can provide you with a list of features that we use in the coming week to help inform a "lite" release.

Thank you again! We really appreciate it!

dotordogh avatar Jul 07 '21 17:07 dotordogh

Hi @bbonanno we have identified that this are the features we depend on:

  • For idiomaticMockito, ArgumentMatchersSugar, ArgCaptor, CallsRealMethod, DefaultAnswer and ResetMocksAfterEachTest we need the full feature set;
  • With MockitoSugar we use verify and when;
  • And for ScalaFirstStubbing we use thenReturn

Thanks again for all your help!

NataliaMGonzalez avatar Aug 27 '21 15:08 NataliaMGonzalez

Yeah, pretty much anything that uses macros :( Will give it a go again as soon as I have some free time (quite scarce lately) Sorry about the wait!

ultrasecreth avatar Sep 10 '21 13:09 ultrasecreth

Hi @bbonanno, do you have any news about this?

jveldhuizen avatar Jan 18 '22 10:01 jveldhuizen

@jveldhuizen I'm afraid not, I've been pretty busy at work and had 0 time. Maybe someone in the community wants to step up? (would be also nice to raise the bus factor to at least 2)

ultrasecreth avatar Jan 18 '22 13:01 ultrasecreth

Any update?

jrgleason avatar Jul 22 '22 14:07 jrgleason

I'm afraid I'm not actively developing the lib anymore, just basic maintenance atm, so no clear path to get a Scala 3 version any time soon... Happy to guide contributors if anyone wants to step in

ultrasecreth avatar Jul 22 '22 16:07 ultrasecreth

@ultrasecreth has anything change since last update? there are still no plans to support Scala 3, right? Do you know what level of effort is required there? Have you tried estimating it? Thanks!

jozic avatar Jul 24 '23 01:07 jozic

I understand that mockito-scala depends heavily on macros, migrating the code to scala3 isn't likely trivial.

https://github.com/fmonniot/scala3mock is an alternative which you could consider, in our case, we ended up using java mockito.

AlexITC avatar Jul 24 '23 02:07 AlexITC

@alexITC

in our case, we ended up using java mockito.

Would love to read a 'how to convert' style blog post if you find yourself so motivated! Is it ugly?

henricook avatar Jan 30 '24 18:01 henricook

We also moved to pure Mockito and/or ScalatestPlus-Mockito. Relatively easy but I guess we were not strongly relying on Mockito scala's specific feature.

gaeljw avatar Jan 30 '24 18:01 gaeljw

EDITED: To add a mock and lazy doNothing method

I've actually just done this, it really was quite easy, but we weren't using mockito-scala matchers. So most of our matchers were already java friendly, and it was mostly just changing mock[Class] to mock(classOf[Class]) and imports

I found that I had to implement a special matcher for Option argument matchers, here it is in case it helps anyone else (or if anyone else has a better way!)

import org.mockito.ArgumentMatchers.argThat
import org.mockito.Mockito
import org.mockito.stubbing.{Answer, Stubber}

import scala.reflect.ClassTag

/** Minimal helper functions to make Java Mockito slightly more scala
  */
object CustomMockito {

  // This method might struggle to mock complex generic types due to type erasure. If experiencing issues
  // Try using Mockito.mock directly with mock[MyClass]
  def mock[T: ClassTag]: T = Mockito.mock(implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]])

  def mock[T: ClassTag](defaultAnswer: Answer[Object]): T =
    Mockito.mock(implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]], defaultAnswer)

  object Stubber {
    def doNothing: Stubber = Mockito.doNothing()
  }

  object Matchers {

    def someMatcher[T]: Option[T] = argThat((arg: Option[T]) =>
      arg match {
        case null    => false // Java interop
        case None    => false
        case Some(_) => true
      }
    )

    def noneMatcher[T]: Option[T] = argThat((arg: Option[T]) =>
      arg match {
        case null => true // Java interop
        case None => true
        case _    => false
      }
    )
  }
}

Usage like:

...
someMatcher[String]
noneMatcher[String]
doNothing
mock[MyClass]
mock[MyClass](Answers.RETURNS_SMART_NULLS)
...

henricook avatar Jan 30 '24 19:01 henricook