为Spark 程序添加单元测试


相比于传统代码,Spark是比较难调试的。程序运行在集群中,每次修改代码后,都要上传到集群进行测试,代价非常大,所以优先在本地进行单元测试,可以减少小模块的逻辑错误。

一、ScalaTest 测试框架

ScalaTest是比JUnit和TestNG更加高阶的测试编写工具,这个Scala应用在JVM上运行,可以测试Scala以及Java代码。ScalaTest一共提供了七种测试风格,分别为:FunSuite,FlatSpec,FunSpec,WordSpec,FreeSpec,PropSpec和FeatureSpec。
FunSuite的方式较为灵活,而且更符合传统测试方法的风格,区别仅在于test()方法可以接受一个闭包。而FlatSpec和FunSpec则通过提供诸如it、should、describe等方法,来规定书写测试的一种模式。
使用方式如下:
1、引入依赖

  testCompile group: 'org.scalatest', name: 'scalatest_2.11', version: '3.0.6-SNAP5' 

2、编写对应的case

scala test 测试代码

import org.scalatest._
class HelloWorldTest
  extends FunSuite  {
  test("Test difference") {
    val a = Set("a", "b", "a", "c")
    val b = Set("b", "d")
    assert(a -- b === Set("a", "c"))
  }

  //交集
  test("Test intersection") {
    val a = Set("a", "b", "a", "c")
    val b = Set("b", "d")
    assert(a.intersect(b) === Set("b"))
  }
 
  //并集
  test("Test union") {
    val a = Set("a", "b", "a", "c")
    val b = Set("b", "d")
    assert(a ++ b === Set("a", "b", "c", "d"))
  }
 
  test("Test difference failed") {
    val a = Set("a", "b", "a", "c")
    val b = Set("c", "d")
    //应该等于Set("a","b")
    assert(a -- b === Set("a", "c"))
  }
}

3、运行该test cast,查看执行效果
在这里插入图片描述

可以看到 前三条case 执行通过,最后一条未通过的case 也明确指出了期望值和实际值,这时候我们 根据提示去修改对应的处理逻辑代码即可。

二、Spark Application 该如何进行单元测试?

通常一个完整的spark application,可以简化的理解为是对一些数据的处理/运算 然后将运算的结果输出到其它介质中。

因此在spark 程序中,会涉及到:
1、对数据的读入(通过streming 或者 批量读文件的形式);
2、对数据进行运算(格式转换、逻辑运算,筛选filter etc);
3、对运算结果的输出

在spark 2.x 的架构中,spark 程序入库已经统一到SparkSession 、Dataset/DataFrame 为主要的用户 API。我们所进行的数据计算操作都可以抽象的理解为是在对 Dataset 进行转换操作,通过我们编写的函数,从一个原 Dataset/DataFrame 得到一个我们所期望的目的 Dataset/DataFrame

我们在编写spark 程序的时候,应该尽量按照这个原则来构建spark 程序内部的功能模块,即:输入输出独立于业务计算,每个业务计算 通过Dataset/DataFrame 的转换来封装成不同的函数模块。那么我们在进行单元测试的时候,就可以只重点关注到 我们实现的一个个具体业务计算的Dataset/DataFrame的转换中,减少 输入输出 等环境依赖(输入输出的格式,依靠接口文档)。

前面 提到的 ScalaTest 测试框架 更侧重于对scala 语法本身的一些 集合,功能进行单元测试。上面我们分析了,对spark 进行单元测试的重心其实就是对 Dataset/DataFrame 转换的测试。

三、Spark Fast Tests 组件

spark-fast-tests 是一个开源工具包,能够很方便帮助我们 对比Dataset/DataFrame。其提供了以下一些方法:
两个Dataset/DataFrame 相等对比
assertSmallDataFrameEquality 适用于本地较小两个DF对比
assertSmallDataFrameEquality 适用于本地较小两个DS对比
assertLargeDatasetEquality 适用于集群较大两个DF对比
assertLargeDatasetEquality 适用于集群较大两个DS对比
⚠️相等比较时候 忽略 null 使用 ignoreNullable 参数
eg:
assertSmallDatasetEquality(actualDF, expectedDF,ignoreNullable=true)

一个 Dataset/DataFrame 两列相等对比
assertColumnEquality(df, "Column1", "Column2")

使用实例:

import org.apache.spark.sql.SparkSession
trait SparkSessionTestWrapper {
  lazy val spark: SparkSession = {
    SparkSession
      .builder()
      .master("local")
      .appName("spark test example")
      .getOrCreate()
  }
}
import com.github.mrpowers.spark.fast.tests._
import org.scalatest.FunSpec
class DatasetSpec extends FunSpec with SparkSessionTestWrapper with DatasetComparer with ColumnComparer {
  import spark.implicits._
  it("aliases a DataFrame") {
    val sourceDF = Seq(
      ("jose"), ("li"),("luisa")
    ).toDF("name")
    val actualDF = sourceDF.selectExpr("name as student")
    val expectedDF = Seq(
      ("jose"),("li"),("luisa")
    ).toDF("student")
    assertSmallDatasetEquality(actualDF, expectedDF)
  }

  it("df expected_name") {
    val df = Seq(
      ("jose", "jose"),
      ("li", "none"),
      ("luisa", "luisa")
    ).toDF("name", "expected_name")
    assertColumnEquality(df, "Column1", "Column2")
  }
}

在这里插入图片描述
运行结果上,可以看到 第一条case 执行通过,最后一条未通过的case 也明确指出了期望值和实际值,这时候我们 根据提示去修改对应的处理逻辑代码即可。
参考资料:
https://blog.csdn.net/hany3000/article/details/51033610
https://medium.com/@mrpowers/testing-spark-applications-8c590d3215fa
https://github.com/MrPowers/spark-fast-tests

### 关于ArcGIS License Server无法启动的解决方案 当遇到ArcGIS License Server无法启动的情况时,可以从以下几个方面排查并解决问题: #### 1. **检查网络配置** 确保License Server所在的计算机能够被其他客户端正常访问。如果是在局域网环境中部署了ArcGIS Server Local,则需要确认该环境下的网络设置是否允许远程连接AO组件[^1]。 #### 2. **验证服务状态** 检查ArcGIS Server Object Manager (SOM) 的运行情况。通常情况下,在Host SOM机器上需将此服务更改为由本地系统账户登录,并重启相关服务来恢复其正常工作流程[^2]。 #### 3. **审查日志文件** 查看ArcGIS License Manager的日志记录,寻找任何可能指示错误原因的信息。这些日志可以帮助识别具体是什么阻止了许可证服务器的成功初始化。 #### 4. **权限问题** 确认用于启动ArcGIS License Server的服务账号具有足够的权限执行所需操作。这包括但不限于读取/写入特定目录的权利以及与其他必要进程通信的能力。 #### 5. **软件版本兼容性** 保证所使用的ArcGIS产品及其依赖项之间存在良好的版本匹配度。不一致可能会导致意外行为或完全失败激活license server的功能。 #### 示例代码片段:修改服务登录身份 以下是更改Windows服务登录凭据的一个简单PowerShell脚本例子: ```powershell $serviceName = "ArcGISServerObjectManager" $newUsername = ".\LocalSystemUser" # 替换为实际用户名 $newPassword = ConvertTo-SecureString "" -AsPlainText -Force Set-Service -Name $serviceName -StartupType Automatic New-ServiceCredential -ServiceName $serviceName -Account $newUsername -Password $newPassword Restart-Service -Name $serviceName ``` 上述脚本仅作为示范用途,请依据实际情况调整参数值后再实施。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值