Closed NPCRUS closed 1 week ago
Hello there,
Ooh, that's a fun one. Indeed I never had to mock a method with a default value and so it's not implemented. It's interesting to see how Scala hack around the fact that default values do not exist in Java land.
Even more interesting is how this is used by the compiler. If we print the AST of a trait with a default method (for example with PrintAst[TestDefaultParameters]
) we see the following:
@scala.annotation.internal.SourceFile("core/src/test/scala/fixtures/TestDefaultParameters.scala") trait TestDefaultParameters() extends java.lang.Object {
def foo(bar: scala.Int): scala.Predef.String
def foo$default$1: scala.Int @scala.annotation.unchecked.uncheckedVariance = 9
}
So it seems like the compilere store the default parameters in methods alongside the main one and use a methodName$default$position
scheme to do so. We can also see how the compiler uses those synthetic methods with
PrintAst {
val t = new TestDefaultParameters {}
t.foo()
}
which prints
{
val t: fixtures.TestDefaultParameters = {
final class $anon() extends fixtures.TestDefaultParameters
(new $anon(): fixtures.TestDefaultParameters)
}
t.foo(t.foo$default$1)
}
So a priori detecting if a method is generated by the compiler or not should be enough. In those cases we simply ignore them and let the compiler do its magic. Lemme see if I can find something.
I have merged a PR that, I believe, should solve your issue. It's available as a snapshot (version 0.6.1+9-5f0d9d1c-SNAPSHOT
) if you want to give it a shot and let me know if that does the trick.
@fmonniot thanks for such a promt response and fix!
I'm not sure where can I get the snapshot from(or how): i get this in sbt:
[error] not found: https://repo1.maven.org/maven2/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/scala3mock_3-0.6.1+9-5f0d9d1c-SNAPSHOT.pom
the maven repo also doesn't contain any snapshots
Ah yes, snapshots aren't publish to maven central.
You'll have to temporarily add resolvers += Resolver.sonatypeRepo("snapshots")
to your project's resolvers.
[error] (ssExtractDependencies) sbt.librarymanagement.ResolveException: Error downloading eu.monniot:scala3mock_3:0.6.1+9-5f0d9d1c-SNAPSHOT
[error] Not found
[error] Not found
[error] not found: ???\.ivy2\local\eu.monniot\scala3mock_3\0.6.1+9-5f0d9d1c-SNAPSHOT\ivys\ivy.xml
[error] not found: https://repo1.maven.org/maven2/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/scala3mock_3-0.6.1+9-5f0d9d1c-SNAPSHOT.pom
[error] not found: https://oss.sonatype.org/content/repositories/snapshot/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/scala3mock_3-0.6.1+9-5f0d9d1c-SNAPSHOT.pom
[error] Total time: 3 s, completed Jun 23, 2024, 3:13:36 PM
although:
[info] downloading https://oss.sonatype.org/content/repositories/snapshot/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/scala3mock_3-0.6.1+9-5f0d9d1c-SNAPSHOT.pom
[info] downloaded https://oss.sonatype.org/content/repositories/snapshot/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/scala3mock_3-0.6.1+9-5f0d9d1c-SNAPSHOT.pom
[info] downloading https://oss.sonatype.org/content/repositories/snapshot/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/scala3mock_3-0.6.1+9-5f0d9d1c-SNAPSHOT.pom.sha1
[info] downloaded https://oss.sonatype.org/content/repositories/snapshot/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/scala3mock_3-0.6.1+9-5f0d9d1c-SNAPSHOT.pom.sha1
[info] downloading https://oss.sonatype.org/content/repositories/snapshot/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/maven-metadata.xml
[info] downloaded https://oss.sonatype.org/content/repositories/snapshot/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/maven-metadata.xml
[info] downloading https://oss.sonatype.org/content/repositories/snapshot/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/maven-metadata.xml.sha1
[info] downloaded https://oss.sonatype.org/content/repositories/snapshot/eu/monniot/scala3mock_3/0.6.1+9-5f0d9d1c-SNAPSHOT/maven-metadata.xml.sha1
sorry, I don't really know how this snapshot extra resolver logic in sbt suppose to work this is my sbt file:
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / scalaVersion := "3.4.2"
lazy val root = (project in file("."))
.settings(
name := "soundless-paygrond",
resolvers += Resolver.sonatypeRepo("snapshot") ,
libraryDependencies ++= Seq(
"dev.soundness" % "wisteria-core" % "0.3.0",
"dev.soundness" % "contingency-core" % "0.1.0",
"eu.monniot" % "scala3mock" % "0.6.1+9-5f0d9d1c-SNAPSHOT"
),
)
The SBT configuration should work, you are just missing a s
at the end of snapshot
.
it's the same result with snapshots
Weird, I can use the snapshot without issue with scastie: https://scastie.scala-lang.org/ud4DIkrwTgq4zhr0HMgbgA. Maybe something else is going on?
I love sbt, it was the %%
in the first part of the dependency. I tested it, it works perfectly. Thank you very much again for promt reaction. I guess I'm gonna look out for the next release then!
Hello again @fmonniot. Unfortunatelly I think we only scrapped the surface of this issue:
class Service {
def test(account: Int, default: Int = 0): Int = ???
}
putting default parameter into second position and so forth returns this issue again. debug output:
Tree Code: {
class ServiceMock extends de.spendit.commons.Test.Service with eu.monniot.scala3mock.context.Mock {
private val mocks: scala.collection.immutable.Map[scala.Predef.String, eu.monniot.scala3mock.functions.MockFunction] = scala.Predef.Map.from[java.lang.String, eu.monniot.scala3mock.functions.MockFunction](new scala.Tuple2[java.lang.String, eu.monniot.scala3mock.functions.MockFunction]("create$default$2-0", new eu.monniot.scala3mock.functions.MockFunction0[scala.Int @scala.annotation.unchecked.uncheckedVariance](contextual$1, "<Test.scala#L19> Service.create$default$2")(eu.monniot.scala3mock.Default.given_Default_Int)), new scala.Tuple2[java.lang.String, eu.monniot.scala3mock.functions.MockFunction]("create$default$2-1", new eu.monniot.scala3mock.functions.MockFunction0[scala.Int @scala.annotation.unchecked.uncheckedVariance](contextual$1, "<Test.scala#L19> Service.create$default$2")(eu.monniot.scala3mock.Default.given_Default_Int)), new scala.Tuple2[java.lang.String, eu.monniot.scala3mock.functions.MockFunction]("create", new eu.monniot.scala3mock.functions.MockFunction2[scala.Int, scala.Int, scala.Int](contextual$1, "<Test.scala#L19> Service.create")(eu.monniot.scala3mock.Default.given_Default_Int)))
def accessMockFunction(name: java.lang.String): eu.monniot.scala3mock.functions.MockFunction = ServiceMock.this.mocks.apply(name)
def create$default$2: scala.Int @scala.annotation.unchecked.uncheckedVariance = ServiceMock.this.mocks.apply("create$default$2-0").asInstanceOf[eu.monniot.scala3mock.functions.MockFunction0[scala.Int @scala.annotation.unchecked.uncheckedVariance]].apply()
override def create$default$2: scala.Int @scala.annotation.unchecked.uncheckedVariance = ServiceMock.this.mocks.apply("create$default$2-1").asInstanceOf[eu.monniot.scala3mock.functions.MockFunction0[scala.Int @scala.annotation.unchecked.uncheckedVariance]].apply()
override def create(account: scala.Int, default: scala.Int): scala.Int = ServiceMock.this.mocks.apply("create").asInstanceOf[eu.monniot.scala3mock.functions.MockFunction2[scala.Int, scala.Int, scala.Int]].apply(account, default)
}
(new ServiceMock(): de.spendit.commons.Test.Service & eu.monniot.scala3mock.context.Mock)
}
Oh that's interesting. I misunderstood how the compiler assign the names. It's actually using the position of the parameter as the indice.
https://github.com/fmonniot/scala3mock/pull/57 should fix this. A snapshot containing this fix is available with the version 0.6.2+4-83ca10ea-SNAPSHOT
.
I have published 0.6.3 if you want to move off the snapshot channel.
Cheers @fmonniot. First of all - thank you for great work on this library, it was a bliss to find it. Now to the point: I think mocking methods with default parameters are currently broken with following error:
value mocks cannot be accessed as a member of (Service2Mock.this : Service2Mock) from class Service2. [error] | private value mocks can only be accessed from class Service2Mock.
I've made a smallest reproducible example of this issue:here is the debugging output together with error:
If you don't have time, let me know - I'm willing to take a stab at this issue myself