ScalaMock
ScalaMock copied to clipboard
ScalaMock macro does not infer/resolve context-bound type-parameters correctly
ScalaMock Version (e.g. 3.5.0)
5.2.0
Scala Version (e.g. 2.12)
2.13.8
Runtime (JVM or JS)
JVM
Please describe the expected behavior of the issue
I expect the mock/stub to be generated successfully i.e. no compilation error
Please provide a description of what actually happens
SBT tells it best:
sbt:example-project> testOnly *MinimalScalaMockSpec
...
[info] compiling 1 Scala source to /path/to/example-project/target/scala-2.13/test-classes ...
[error] [E1] src/test/scala/reproducible/example/MinimalScalaMockSpec.scala
[error] type mismatch;
[error] found : reproducible.example.SomeTypeClass[A]
[error] required: reproducible.example.SomeTypeClass[Int]
[error] L36: val dep = stub[ConcreteDep[Int]]
[error] ^
[error] src/test/scala/reproducible/example/MinimalScalaMockSpec.scala: L36 [E1]
[info] Legend: Ln = line n, Cn = column n, En = error n
[error] (Test / compileIncremental) Compilation failed
[error] Total time: 0 s, completed 17 May 2022, 11:55:30
sbt:example-project>
Reproducible Test Case
package reproducible.test.case
import org.scalamock.scalatest.MockFactory
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
trait SomeTypeClass[A0] {
type A = A0
}
trait SomeDependencyWithTypeParameters[A0, B0, C0[_]] {
type A = A0
type B = B0
type C[X] = C0[X]
def getStatus: String
}
class ConcreteDep[A: SomeTypeClass] extends SomeDependencyWithTypeParameters[A, String, List] {
def getStatus: String = "pass"
}
class Composite[A, B, C[_], D0 <: SomeDependencyWithTypeParameters[A, B, C]](
dep: D0
) {
type D = D0
def doSomething(): String = dep.getStatus
}
class MinimalScalaMockSpec extends AnyFlatSpec with Matchers with MockFactory {
"simply" should "mock a dependency with type parameters" in {
type AlternateDep = SomeDependencyWithTypeParameters[Double, Boolean, Option]
val dep = stub[ConcreteDep[Int]]
(() => dep.getStatus).expects().returning { "pass" }.once()
val alt = mock[AlternateDep]
(() => alt.getStatus).expects().returning { "undecided" }.twice()
val first = new Composite[Int, String, List, ConcreteDep[Int]](dep)
val second = new Composite[Double, Boolean, Option, AlternateDep](alt)
first.doSomething() should be(second.doSomething())
}
}