我可以通过隐式类使用其他方法扩展Scala类Foo
:
trait Foo {
def bar: String
}
object FooExtensions {
object implicits {
implicit class FooOps(foo: Foo) {
def baz: String = "baz"
}
}
}
但是我可以模拟那些方法吗?
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
}
}
}
这会编译,但会失败
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.
是否可以模拟通过隐式类添加的方法?希望能与Mockito(或mockito-scala)在一起,但我对任何可行的方法都感兴趣。
答案 0 :(得分:3)
关于扩展方法,基本上就是语法糖:
trait Foo
implicit class ExtensionMethods(foo: Foo) {
def bar: String = "bar
}
foo.bar
等于
new ExtensionMethods(foo).bar
所以嘲笑:
Mockito.when(foo.bar).thenReturn("bix")
成为:
Mockito.when(new ExtensionMethods(foo).bar).thenReturn("bix")
我认为没有解决方法-也许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")
附带说明:功能越多,模拟事物就越少,因为一切都将仅取决于传递给内部的输入,而一连串的模拟只会变成代码的味道-紧密耦合,问题是许多Java库甚至都没有遵循SOLID原则,这使得它们既难以与FP一起使用/测试,又很难单独使用OOP。我是在告诉您这种情况,以防您觉得嘲笑是唯一的解决方法。
答案 1 :(得分:0)
实现此目标的唯一方法是使用隐式转换而不是隐式类
这是一个旨在说明如何实现此目的的黑客工具,但我强烈建议您看一下代码,看看为什么您实际上需要这样做
因此,按照您的示例,您可以将代码修改为如下所示
trait Foo {
def bar: String
}
object FooExtensions {
object implicits {
implicit fooToOps(foo: Foo): FooOps = new FooOps(foo)
class FooOps(foo: Foo) {
def baz: String = "baz"
}
}
}
和您的测试
import org.scalatest.WordSpec
import org.mockito.MockitoSugar
class MySpec extends WordSpec with MockitoSugar {
"My mock" should {
"handle methods from implicit classes" in {
val fooOps = mock[FooOps]
implicit fooToOps(foo: Foo): FooOps = fooOps
val foo = mock[Foo]
when(foo.baz) thenReturn "bix" // works
}
}
}
要考虑的另一件事是,在生产中需要获取形状为Foo => FooOps
的隐式参数,因此,当您从测试中调用该方法时,会提供实际的隐式模拟...
正如我所说,您可以像这样使它工作,但是我同意Mateusz的观点,您不需要