简体   繁体   中英

Scala implicit classes and functions

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.

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(_))
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM