![](/img/trans.png)
[英]Scala/Mockito: How to mock the result of a method called inside another method?
[英]How to mock method inside method in scala object?
我想用一个简单的例子来解决我的问题。
我有以下特点:
trait ReaderTrait {
def readTable(tableName:String, closingDate: String)(implicit sparkSession: SparkSession): DataFrame = {
sparkSession.read.table(
tableName
).filter(col("closingDate") === closingDate)
}
def read(fullTable:String, closingDate: String)(implicit sparkSession: SparkSession): DataFrame
}
我有以下对象:
object Reader extends ReaderTrait
{
override final def read(fullTable: String, closingDate: String)(implicit sparkSession: SparkSession): DataFrame =
{
readTable(fullTable: String, closingDate: String)
//.filter..
//I have business logic here which I want to test, I want to mock only readTable
}
}
现在我只想模拟 Object 中的readTable
方法。
我有一个测试类:
class ReaderTest extends FeatureSpec with MockitoSugar {
scenario("first") {
//sample dataframe1 created ......
val mockTrait = mock[Reader.type ]
when(mockTrait.readTable("abc", "sss")(spark)) thenReturn (dataframe1)
val result = mockTrait.read("abc", "sss")(spark)
result.show()
}
}
运行上述测试类后,它给了我以下错误:
抛出了 java.lang.NullPointerException。
请帮我解决这种情况; 我必须在对象中的 read 方法中模拟readTable
方法,而不对现有代码进行任何更改。
这里有很多问题:
您不能模拟对象。 mock[Reader.type]
没有意义。 不过,您可以使用mock[ReaderTrait]
来模拟特征。
模拟不像你想象的那样工作。
如果你有class A { def foo = ""; def bar = foo }
class A { def foo = ""; def bar = foo }
,然后你做
val m = mock[A]
when(m.foo).thenReturn("baz")
val result = m.bar
result
的值不会是“baz”,而是null
。 为什么? 因为m
是一个mock——一个虚拟对象,所有的方法都被删除了。 m.bar
不调用foo
,因为它是一个存根,它只返回null
。
有一种方法可以只存根类的某些方法,而让其他方法保持原始行为。 如果您想了解更多信息,请查看spy
的概念(您会使用spy(new A)
而不是mock[A]
)。 我不会在这里讨论这个,因为使用间谍不是一个好习惯。 这让我想到了第三点......
在这种情况下,编写测试的正确方法是将readTable
“提供程序”类与您正在测试的功能隔离开来。 例如:
trait TableReader {
def readTable(table:String, date: String)(implicit s: SparkSession) =
s.read.table(table).filter(col("closingDate") === date)
}
object TableReader extends TableReader
class Reader(val tr: TableReader) {
def read(table: String, date: String)(implicit s: SparkSession) =
tr.readTable(table, date)
}
object Reader extends Reader(TableReader)
现在您可以使用以下内容测试您的Reader
类:
val tr = mock[TableReader]
wen(tr.readTable(any, any)(any)).thenReturn(dataframe1)
val reader = new Reader(tr)
reader.read("foo", "bar")(spark) shouldBe dataframe1
verify(tr).readTable("foo", "bar")(spark)
这会起作用,但有点不清楚你在这里要测试的是什么。 你没有测试readTable
因为你把它存根了。 而且read
没有逻辑,所以没有什么可以测试的。
我建议将过滤逻辑从TableReader
移到Reader
中,这样可以更清晰地分离两个类之间的职责:一个负责从表中获取数据,另一个应用过滤和其他业务逻辑:
trait TableReader {
def readTable(t: String)(implicit s: SparkSession) = s.read.table(t)
}
class Reader(tr: TableReader) {
def read(table: String, date: String)(implicit s: SparkSession) =
tr.readTable(table).filter(col("closingDate") === date)
}
这使您能够编写有意义的测试,例如:
reader.read("foo", "bar")(spark)
.select("closingDate")
.as[String]
.collect
.toList shouldBe List("bar", "bar", "bar")
(基本上,检查closingDate === "bar"
的过滤器是否已正确应用于数据框)
这里的想法是您不需要测试TableReader
,因为那里没有任何业务逻辑,它只是 spark 的简单包装器,因此您可以轻松地将其存根。
Reader
类只关心你想要测试的业务逻辑,并不知道实际数据来自哪里,所以你不需要存根它的任何方法,只需提供一个模拟提供程序数据( mock[TableReader]
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.