paulbutcher / ScalaMock

Native Scala mocking framework
http://scalamock.org/
MIT License
501 stars 98 forks source link

Testing scala Parser and get `Parameter type in structural refinement may not refer to an abstract type defined outside that refinement` #500

Open apple-corps opened 5 months ago

apple-corps commented 5 months ago

If you want to discuss a new feature, please ignore/clear this form.

ScalaMock Version (e.g. 3.5.0)

5.2.0

Scala Version (e.g. 2.12)

2.13

Runtime (JVM or JS)

JVM

Please describe the expected behavior of the issue

Trying to mock a trait with scala Parser. Receive error like follows:

Parameter type in structural refinement may not refer to an abstract type defined outside that refinement val mockedCommandDispatcher = mock[UserRepositoryManagementCommandDispatcher]

Reproducible Test Case

Please provide a minimised code snippet that fails, ideally, written as a failing test case in ScalaTest. This will help us a lot in diagnosing the problem and work on a fix. If the issue is more complex or requires configuration, please provide a link to a project on Github that reproduces the issue.

`

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalamock.scalatest.MockFactory
import scala.util.parsing.combinator.RegexParsers
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

trait PareserTrait {
  def findUser(username: String): Future[(String, String)]

  def parseUser: Parser[Future[String]] = ("find-user" ~> word | ":") ^^ { username =>
    findUser(username).map(result => s"${result._1} for username ${result._2}")
  }
}

class ParserTraitTest extends AnyFlatSpec with Matchers with MockFactory with ParserTrait with RegexParsers {

  "ParserTrait" should "parse user correctly with the expected username" in {
    val parserTrait = mock[ParserTrait]

    val expectedUsername = "John"
    val expectedResult = ("User found", expectedUsername)

    (parserTrait.findUser _)
      .expects(expectedUsername)
      .returning(Future.successful(expectedResult))

    val input = s"find-user $expectedUsername"

    val result: Future[String] = parse(parserTrait.parseUser, input) match {
      case Success(parsedResult, _) => parsedResult
      case Failure(msg, _) => Future.failed(new RuntimeException(s"Parsing failed: $msg"))
    }

    // Now you can assert on the parsed result without worrying about the implementation details.
    result.map { parsedValue =>
      parsedValue shouldBe s"${expectedResult._1} for username ${expectedResult._2}"
    }
  }
}