我有一段java代码,其中我调用了一个scala对象的方法(属于一个依赖库)。为了编写单元测试,我想模拟scala对象的方法调用
Operations.scala
object Operations {
def load(spark: SparkSession, path: String): Dataset[Row] = {
}
}Main.java
public class Main {
public void callMethod() {
Dataset<Row> df = Operations.load(sparkSession, path);
}
}我想为callMethod()编写一个单元测试,从而模拟Operations.load scala调用。我没有找到一种方法来做这件事。
任何帮助都将不胜感激。
发布于 2021-06-23 19:09:53
如果您希望更改callMethod方法的签名,一种可能的解决方案是使用接口和依赖项注入。
创建一个为您提供Dataset[Row]的接口
trait ObtainDataSet {
def obtain: DataSet[Row]
}然后让你的callMethod有一个接受这个接口的参数:
public void callMethod(ObtainDataSet obtainDataSet) {
Dataset<Row> df = obtainDataSet.obtain;
}然后在生产代码中使用Operations.load方法注入真正的实现:
class ObtainDataSetImpl(spark: SparkSession, path: String) extends ObtainDataSet {
override def obtain: Dataset[Row] = Operations.load(sparkSession, path)
}但是,您可以很容易地将该生产实现替换为测试中的特别实现。你甚至不需要使用模拟框架:
class MyMock extends ObtainDataSet {
override def obtain: DataSet[Row] = ??? // something useful for tests.
}但如果您愿意,通常会使用ScalaMock
发布于 2021-06-23 19:40:02
这通常是这样做的。我将用scala编写它,因为java的语法要冗长得多(坦率地说,我记不太清楚了),但它的概念是一样的。
trait Operations {
def load(s: SparkSession, path: String): DataSet[Row]
}
object Operations extends Operations {
def load(s: SparkSession, path: String) = ??? // implementation here
}
class Main(operations: Operations = Operations) {
callMethod(): Unit = {
val df = operations.load(sparkSession, path);
...
// do stuff
}
}然后在测试中,您可以编写(这是使用Mockito/scalatest,但同样,对于您用于测试的任何库,想法也是相同的):
val ops = mock[Operations]
when(ops.load(any, any)).thenReturn(whatever)
new Main(ops).callMethod(mock[SparkSession], "/foo/bar")这种方法被称为“依赖注入”:您的Main类对Operations有一个“依赖”,现在是在运行时“注入”,而不是像在代码片段中那样被硬编码。这允许您为生产(实际的Operations对象)和测试(模拟)运行使用不同的依赖项实例。
此外,这是一个单独的主题,但我认为它仍然值得一提:您正在测试的方法的Unit (void)返回类型似乎不是最好的想法,并且可能会在以后的测试中给您带来更多问题(您如何确保该方法实际上产生了您所期望的副作用?)。
最好让此方法返回实际结果,并让调用者处理副作用。然后,您可以很容易地测试它是否产生了您期望的结果:
new Main(ops).callMethod(mock[SparkSession], "/foo/bar") shouldBe "ExpectedFooResult"https://stackoverflow.com/questions/68097510
复制相似问题