如何使用scala模拟Spark DataFrameReader?

如何使用scala模拟Spark DataFrameReader?,scala,unit-testing,apache-spark,mocking,Scala,Unit Testing,Apache Spark,Mocking,我想使用sparkSession.read.jdbc(…)对从RDBMS读取数据帧的代码进行单元测试。但我没有找到一种方法来模拟DataFrameReader以返回虚拟数据帧进行测试 代码示例: object ConfigurationLoader { def readTable(tableName: String)(implicit spark: SparkSession): DataFrame = { spark.read .format("jdbc") .

我想使用
sparkSession.read.jdbc(…)
对从RDBMS读取数据帧的代码进行单元测试。但我没有找到一种方法来模拟DataFrameReader以返回虚拟数据帧进行测试

代码示例:

object ConfigurationLoader {

def readTable(tableName: String)(implicit spark: SparkSession): DataFrame = {
    spark.read
      .format("jdbc")
      .option("url", s"$postgresUrl/$postgresDatabase")
      .option("dbtable", tableName)
      .option("user", postgresUsername)
      .option("password", postgresPassword)
      .option("driver", postgresDriver)
      .load()
  }

def loadUsingFilter(dummyFilter: String*)(implicit spark: SparkSession): DataFrame = {
    readTable(postgresFilesTableName)
      .where(col("column").isin(fileTypes: _*))
  }
}

第二个问题是模拟scala对象,看起来我需要使用其他方法来创建这样的服务。

如果您想测试
sparkSession.read.jdbc(…)
,您可以使用内存中的H2数据库。我有时在写学习测试的时候会这样做。您可以在这里找到一个示例:但是请注意,您可能会遇到与“真实”RDBMS的一些细微差异

另一方面,您可以更好地分离代码的关注点,并以不同的方式创建
DataFrame
,例如使用
toDF(…)
方法。您可以在此处找到一个示例:

最后,在我看来,如果您必须模拟
DataFrameReader
,这意味着可能与代码分离有关。例如,您可以将所有过滤器放在
过滤器
对象中,并分别测试每个过滤器。映射或聚合函数也是如此。2年前,我写了一篇关于测试ApacheSpark的博文——它描述了RDDAPI,但分离关注点的想法是一样的


更新:

对象过滤器{
def isInFileTypes(inputDataFrame:DataFrame,文件类型:Seq[String]):DataFrame={
inputDataFrame.where(列)isin(文件类型:*)
}
}
对象配置加载器{
def readTable(tableName:String)(隐式spark:SparkSession):数据帧={
val输入=spark.read
.格式(“jdbc”)
.选项(“url”,s“$postgresUrl/$postgresDatabase”)
.option(“dbtable”,tableName)
.选项(“用户”,postgresUsername)
.选项(“密码”,postgresPassword)
.选项(“驱动程序”,postgresDriver)
.load()
Filters.isInFileTypes(输入,Seq(“txt”,“doc”)
}
有了它,你就可以随心所欲地测试你的过滤逻辑:)如果你有更多的过滤器并且想测试它们,你也可以在一个方法中组合它们,传递任何你想要的
DataFrame
,瞧:) 除非有很好的理由,否则不应该测试
.load()
。这是已经测试过的Apache Spark内部逻辑


更新,回答:


所以,现在我可以测试过滤器了,但是如何确保readTable真正使用了正确的过滤器(抱歉,这是完全覆盖的问题)。也许你有一些简单的方法来模拟scala对象(这实际上是一个毫秒的问题)。-dytyniak 14分钟前

对象MyApp{
def main(参数:数组[字符串]):单位={
val inputDataFrame=readTable(postgreSQLConnection)
val outputDataFrame=ProcessingLogic.generateOutputDataFrame(inputDataFrame)
}
}
对象处理逻辑{
def generateOutputDataFrame(inputDataFrame:DataFrame):DataFrame={
//在这里,您可以应用所有需要的过滤器、转换和协作
}
}

如您所见,无需在此处模拟
对象
。这似乎是多余的,但这并不是因为您可以通过
过滤器
对象单独测试每个过滤器,以及通过
处理逻辑
对象组合的所有处理逻辑(仅限名称)。并且您可以以任何有效的方式创建
数据帧
。缺点是您需要显式定义架构或使用
案例类
,因为在PostgreSQL源代码中,Apache Spark将自动解析架构(我在这里解释:).

在我看来,单元测试并不意味着测试数据库连接。这应该在集成测试中完成,以检查所有部件是否协同工作。单元测试只是测试您的功能逻辑,而不是spark从数据库读取数据的能力

这就是为什么我会设计出稍微不同的代码,而不关心数据库

/**这个,我不测试。我信任spark.read*/
def readTable(tableName:String)(隐式spark:SparkSession):数据帧={
spark.read
.选项(……)
...
.load()
//没别的了
}
/**这是我的测试,这是我的逻辑*/
def转换(df:DataFrame,dummyFilter:String*):DataFrame={
df
.where(col(“column”).isin(文件类型:*)
}
然后我在生产中以这种方式使用代码

val source=readTable(“…”)
val结果=转换(源、过滤器)
现在,包含我的逻辑的
transform
,很容易测试。如果您想知道如何创建虚拟数据帧,我喜欢的一种方法是:

val df=Seq((1,部分(“a”),真,(2,部分(“b”),假),
(3,无,对)toDF(“x”,“y”,“z”)
//测试呢
val结果=变换(df,过滤器)
结果应该是。。。

谢谢你的回答。我不喜欢使用H2的想法,因为我有不同的存储,这不是通用的方法。我已经在使用分离的方法,但我仍然有一些未涵盖的代码,这就是为什么我问如何模拟阅读器。你能展示你的代码吗?可能与分离有更多的关系?更新的post.C您是否也可以为object.Answeed的第二个问题提供建议。这种分离应该可以解决您的两个问题(或者我没有正确地得到第二个问题)。因此,现在我可以测试过滤器,但如何确保readTable真正使用正确的过滤器(抱歉,这只是完全覆盖的问题)。您可能有一些简单的方法来模拟scala对象(这实际上是第二个问题)。感谢您的回答。您可以参与对上一个答案的评论。我们正在讨论与您类似的方法。我错过了上一个答案上的评论:)我当时对它不完全满意(现在有了编辑,我更喜欢它)所以我在一个单独的答案中分享了我的观点。还有什么不清楚的吗?如果你有关于测试的问题,请不要犹豫。我只想