将 scalatest 与 before/after 一起使用时如何避免 'null'?

How to avoid 'null' when use scalatest with before/after?

从这个article about spark testing,有一些示例代码:

class SparkExampleSpec extends FlatSpec with BeforeAndAfter {

  private val master = "local[2]"
  private val appName = "example-spark"

  private var sc: SparkContext = _

  before {
    val conf = new SparkConf()
      .setMaster(master)
      .setAppName(appName)

    sc = new SparkContext(conf)
  }

  after {
    if (sc != null) {
      sc.stop()
    }
  }
  (...)

它有效,但有一个可为 null 的变量 sc。这里很合理,但我还是想避免。

我尝试使用:

private var sc: Option[SparkContext] = None

  before {
    sc = Some(new SparkContext(conf))
  }

  after {
    sc.foreach(_.stop())
  }
  (...)

但问题是,我必须在测试中使用sc作为Option[SparkContext],这不像普通的那样方便SparkContext

有没有办法让测试继续工作,但我们可以使用 val sc: SparkContext?

(我知道在 specs2 中可以,但不知道如何在 scalatest 中实现,我们现在必须使用 scalatest)

您可以放弃使用 before,而只使用 lazy val,这样当第一个测试调用它时,上下文就会被初始化:

class SparkExampleSpec extends FlatSpec with BeforeAndAfter {

  private val master = "local[2]"
  private val appName = "example-spark"

  private lazy val conf = new SparkConf()
    .setMaster(master)
    .setAppName(appName)
  private lazy val sc = new SparkContext(conf)

  after {
    sc.stop()
  }

更新:

所以我 运行 以前做过类似的事情,我们最终得到了如下特征模式:

trait SparkAddOn {

  val conf:SparkConf

  def withSpark(f: SparkContext => Unit) ={
    val sc: SparkContext = new SparkContext(conf)
    try{
      f(sc)
    } finally {
      sc.stop()
    }
  }
}

那么在你的测试中你只需使用 'withSpark':

class SparkExampleSpec extends FlatSpec with SparkAddOn {

  private val master = "local[2]"
  private val appName = "example-spark"

  val conf = new SparkConf()
    .setMaster(master)
    .setAppName(appName)

  "test" should "do something" in withSpark { sc =>

  }
}

最后我会说我更喜欢 Specs2 而不是 Scalatest,设计截然不同而且更好,抱歉你不能切换。