是否可以模拟 Scala 隐式类?

Sas*_*lla 0 scala implicit mockito

我可以Foo通过隐式类使用其他方法扩展我的 Scala类:

trait Foo {
  def bar: String
}

object FooExtensions {
  object implicits {
    implicit class FooOps(foo: Foo) {
      def baz: String = "baz"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但是我可以模拟这些方法吗?

import org.mockito.Mockito
import org.scalatest.WordSpec
import org.scalatest.mockito.MockitoSugar

class MySpec extends WordSpec with MockitoSugar {
  "My mock" should {
    "handle methods from implicit classes" in {
      import FooExtensions.implicits._
      val foo = mock[Foo]
      Mockito.when(foo.baz).thenReturn("bix") // fails at runtime
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这编译,但失败

when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
   Those methods *cannot* be stubbed/verified.
   Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
   Those methods *cannot* be stubbed/verified.
   Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
Run Code Online (Sandbox Code Playgroud)

是否可以模拟通过隐式类添加的方法?希望与 Mockito (或mockito-scala),但我对任何有效的方法感兴趣。

Mat*_*zok 5

关于扩展方法,它们基本上是一种语法糖:

trait Foo
implicit class ExtensionMethods(foo: Foo) {
  def bar: String = "bar
}

foo.bar
Run Code Online (Sandbox Code Playgroud)

等于

new ExtensionMethods(foo).bar
Run Code Online (Sandbox Code Playgroud)

这么嘲讽:

Mockito.when(foo.bar).thenReturn("bix")
Run Code Online (Sandbox Code Playgroud)

变成:

Mockito.when(new ExtensionMethods(foo).bar).thenReturn("bix")
Run Code Online (Sandbox Code Playgroud)

我认为没有解决方法-也许 PowerMock 可以让您更改类构造函数...,但是使用普通的 Mockito 是不可能的。

通常,这不是问题。那是因为:

  • 您将扩展方法行为放入扩展方法中,这仅取决于扩展值和传递的参数(扩展方法通常是不需要模拟的纯函数)-如果您想在那里更改某些内容,则更改输入,
  • 如果行为应该改变,则在类型类中实现它,并使扩展方法使用该类型类来注入行为

    trait Bar {
      def bar: String
    }
    object Bar {
      implicit val defaultBar: Bar = new Bar { def bar = "bar" }
    }
    
    implicit class ExtensionMethods(foo: Foo) {
      def bar(implicit bar: Bar): String = bar.bar
    }
    
    // in test
    implicit var overridenBar: Bar = ...
    assert(foo.bar === "sth")
    
    Run Code Online (Sandbox Code Playgroud)

附带说明:您获得的功能越多,您需要模拟的东西就越少,因为一切都将仅取决于内部传递的输入,并且一连串的模拟将变成一种代码味道 - 耦合太紧,接口太大等。问题是许多 Java 库甚至不遵循 SOLID 原则,这使得它们既难以使用 FP 进行使用/测试,也难以单独使用糟糕的 OOP。如果你觉得嘲笑是你的情况的唯一途径,我会告诉你这个。