Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala implicit classes and functions

Tags:

scala

implicit

I'm trying to refactor some code which uses an implicit class. Basically, it's code for some unit tests in which I do some mockying and spying with Mockito.

In the below example, the mocking of those functions is done in the implicit class A. Each method "overrideX" takes an expected result, overrides the call to method X in myObj and returns myObj (builder pattern for easy chaining multiple overrides later).

object Outer {
  "call to abc" should {
    "fail if call to X fails" in {
      val failReason = "X failed"
      val myObjMock = B

      myObjMock.overrideX(failReason)
      myObjMock.abc mustEqual failReason
    }

    "fail if call to Y fails" in {
      val failReason = "Y failed"
      val myObjMock = B

      myObjMock.overrideY(failReason)
      myObjMock.abc mustEqual failReason
    }
  }

  implicit class A(myObj: B) {
    def overrideX(result: Result): B = {
      //override call to X in myObj with result
      myObj
    }

    def overrideY(result: Result): B = {
      //override call to Y in myObj with result
      myObj
    }
  }
}    

The problem that I'm trying to solve is to eliminate a lot of boiler plate in the tests. As you can see in the above example, those two tests look very similar. What I would like to achieve is to create some method common for both which would take the override method and do the actual check.

def somethingElse(result: Result, f: Result => B) = {
    val myObjMock = B
    myObjMock.f(result).abc mustEqual result
}

The tests would look something like

"call to abc" should {
    "fail if call to X fails" in {
      val failReason = "X failed"
      somethingElse(failReason, overrideX)
    }

    "fail if call to Y fails" in {
      val failReason = "Y failed"
      somethingElse(failReason, overrideY)
    }
  }

I know that the overrideX and overrideY methods are on the class and not on a companion object.

I currently have a version in which the overrideXX methods are in a companion object and take the result and the validator as parameters. But they are not implicit and I had to drop the builder pattern, so no chaining on them.

like image 245
Tudor Mazilu Avatar asked Jul 17 '26 13:07

Tudor Mazilu


1 Answers

I think what you need is a function (B, Result) => B instead of Result => B. If you have a variable f that contains a function you cannot call it on an object like myObjMock.f(...). But you can pass myObjMock into f, like f(myObjMock, ...).

def somethingElse(result: Result, f: (B, Result) => B) = {
  val myObjMock = B
  f(myObjMock, result).abc mustEqual result
}

"call to abc" should {
  "fail if call to X fails" in {
    val failReason = "X failed"
    somethingElse(failReason, _.overrideX(_))
  }

  "fail if call to Y fails" in {
    val failReason = "Y failed"
    somethingElse(failReason, _.overrideY(_))
  }
}
like image 181
Jasper-M Avatar answered Jul 19 '26 02:07

Jasper-M



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!