You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scala.js测试套件之间共享数据?

Scala.js测试套件间共享数据的解决方案

问题背景

在ScalaTest中,通过公共单例对象SharedTestData在测试套件间共享数据,JVM环境下可正常工作,但Scala.js环境中完全无效——因为Scala.js的每个测试套件会启动全新的程序执行实例,单例对象的状态无法跨实例共享。

示例代码:

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

object SharedTestData {
  var useCount = 0

  def useMe(): Int = {
    useCount += 1
    useCount
  }
}

class ATest extends AnyFlatSpec with Matchers {
  "This" should "pass" in {
    val used = SharedTestData.useMe()
    info(s"Used $used times")
  }
}

class BTest extends AnyFlatSpec with Matchers {
  "This" should "pass as well" in {
    val used = SharedTestData.useMe()
    info(s"Used $used times")
  }
}

执行sbt test后的输出:

[info] BTest:
[info] This
[info] - should pass as well
[info]   + Used 1 times
[info] ATest:
[info] This
[info] - should pass
[info]   + Used 1 times
[info] Run completed in 422 milliseconds.
[info] Total number of tests run: 2

当需要共享的是耗时数分钟的计算结果时,重复计算会极大降低测试效率,因此需要可靠的跨套件数据共享方案。


可行解决方案

1. 利用运行环境的持久化存储(浏览器/Node.js)

Scala.js测试运行在浏览器或Node.js环境中,可借助这些环境的持久化机制实现跨套件状态共享:

浏览器环境(使用localStorage)

改造SharedTestData,将状态存储在浏览器的localStorage中:

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import scala.scalajs.js

object SharedTestData {
  private val storageKey = "shared_test_use_count"

  private def currentCount: Int = {
    js.Dynamic.global.localStorage.getItem(storageKey).asInstanceOf[js.UndefOr[String]]
      .map(_.toInt)
      .getOrElse(0)
  }

  def useMe(): Int = {
    val newCount = currentCount + 1
    js.Dynamic.global.localStorage.setItem(storageKey, newCount.toString)
    newCount
  }

  // 测试前重置状态,避免影响后续测试运行
  def reset(): Unit = {
    js.Dynamic.global.localStorage.removeItem(storageKey)
  }
}

// 在测试类中添加初始化/清理逻辑
class ATest extends AnyFlatSpec with Matchers {
  override def beforeAll(): Unit = {
    // 仅在第一个运行的套件中重置,可根据需求调整
    if (SharedTestData.currentCount == 0) {
      SharedTestData.reset()
    }
  }

  "This" should "pass" in {
    val used = SharedTestData.useMe()
    info(s"Used $used times")
  }
}

class BTest extends AnyFlatSpec with Matchers {
  "This" should "pass as well" in {
    val used = SharedTestData.useMe()
    info(s"Used $used times")
  }
}

Node.js环境(使用文件系统)

在Node.js环境下,用文件系统存储共享数据:

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport

// 引入Node.js的fs模块
@JSImport("fs", JSImport.Namespace)
@js.native
object FS extends js.Object {
  def readFileSync(path: String, encoding: String): String = js.native
  def writeFileSync(path: String, data: String): Unit = js.native
  def existsSync(path: String): Boolean = js.native
}

object SharedTestData {
  private val dataFile = "./test-shared-data.json"

  private def currentCount: Int = {
    if (FS.existsSync(dataFile)) {
      val content = FS.readFileSync(dataFile, "utf-8")
      js.JSON.parse(content).asInstanceOf[js.Dynamic].count.asInstanceOf[Int]
    } else 0
  }

  def useMe(): Int = {
    val newCount = currentCount + 1
    FS.writeFileSync(dataFile, js.JSON.stringify(js.Dynamic.literal(count = newCount)))
    newCount
  }

  def reset(): Unit = {
    if (FS.existsSync(dataFile)) {
      val unlink = js.Dynamic.global.fs.unlinkSync.asInstanceOf[String => Unit]
      unlink(dataFile)
    }
  }
}

// 测试类中添加重置逻辑
class ATest extends AnyFlatSpec with Matchers {
  override def beforeAll(): Unit = {
    SharedTestData.reset()
  }

  "This" should "pass" in {
    val used = SharedTestData.useMe()
    info(s"Used $used times")
  }
}

2. 合并相关测试到同一个套件

如果测试逻辑允许,将需要共享数据的测试用例合并到同一个AnyFlatSpec类中,所有测试用例会在同一个Scala.js执行实例中运行,单例对象状态可正常共享:

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

object SharedTestData {
  var useCount = 0

  def useMe(): Int = {
    useCount += 1
    useCount
  }
}

class SharedDataSpec extends AnyFlatSpec with Matchers {
  "Test A" should "pass" in {
    val used = SharedTestData.useMe()
    info(s"Used $used times")
  }

  "Test B" should "pass as well" in {
    val used = SharedTestData.useMe()
    info(s"Used $used times")
  }
}

3. 预计算共享数据并作为测试资源

对于耗时的计算,可在测试执行前预先计算好数据,保存为静态资源文件(如JSON、二进制文件),所有测试套件直接读取该文件即可。这种方式适合数据不随测试变化的场景:

  • 在sbt中添加预计算任务,在test任务前执行,生成共享数据文件
  • 测试套件中读取该文件加载数据,无需重复计算

内容的提问来源于stack exchange,提问作者Suma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 11:17:49