yggdrasil.spark.tests¶
tests ¶
Unittest base class for PySpark tests.
Provides a single global SparkSession shared across all SparkTestCase subclasses in the process. Spark startup is expensive (~5-10s) — creating a session per test class would make the suite painfully slow.
Quick start¶
::
from yggdrasil.spark.tests import SparkTestCase
class TestMyStuff(SparkTestCase):
def test_basic(self):
df = self.spark.createDataFrame([(1, "a")], ["id", "val"])
self.assertEqual(df.count(), 1)
def test_arrow_roundtrip(self):
import pyarrow as pa
tbl = pa.table({"id": [1, 2], "val": ["a", "b"]})
df = self.arrow_to_spark(tbl)
self.assertSparkEqual(df, tbl)
def test_with_scratch_path(self):
# self.tmp_path is a fresh per-test directory, cleaned up after
out = self.tmp_path / "data.parquet"
self.spark.range(10).write.parquet(str(out))
pytest users¶
The module also exposes a spark fixture (session-scoped) so you can
skip the class hierarchy if you prefer::
def test_something(spark):
assert spark.range(5).count() == 5
Design notes¶
- Session creation goes through :meth:
PyEnv.spark_sessionwithconnect=False(force local-only — tests must not accidentally hit a Databricks workspace just becauseDATABRICKS_HOSThappens to be set in the environment).tearDownClassnever stops the session. - Because the session is shared,
spark_extra_configonly takes effect for the first class that triggers creation. - Arrow interop uses
spark.sql.execution.arrow.pyspark.enabled=trueby default — zero-copy-ish transfer for supported types.
SparkTestCase ¶
Bases: TestCase
Base class for Spark integration tests.
A single global SparkSession is created on first use and shared across
every subclass in the process. Each test method also gets a fresh
self.tmp_path (pathlib.Path) that is cleaned up in tearDown.
Attributes¶
spark : SparkSession
The shared global session. Populated by setUpClass.
tmp_path : pathlib.Path
Per-test scratch directory. Populated by setUp.
Class attributes¶
spark_app_name : str Spark application name. Only effective for the first class that triggers session creation. spark_extra_config : dict[str, str] Extra Spark config entries. Same caveat — only the first class wins.
is_spark_connect
classmethod
¶
True when the shared session is a Spark Connect (Databricks Connect) session rather than a local-JVM one.
udf_supported
classmethod
¶
Whether Python UDFs can execute against the shared session.
Always true on a local-JVM session. On Spark Connect a Python UDF only runs when the client and server share the same minor Python version — otherwise the server rejects it ("client and server should have the same minor Python version"). Probed once (a tiny UDF round-trip) and cached globally, since the session is shared across the whole process.
skip_if_no_udf ¶
skip_if_no_udf(
reason: str = "Python UDFs need a matching client/server Python minor version on Spark Connect",
) -> None
Skip the current test when Python UDFs can't run on the session (Spark Connect with a client/server Python-version mismatch).
skip_if_spark_connect ¶
skip_if_spark_connect(
reason: str = "JVM-only API (sparkContext / rdd) is unavailable on Spark Connect",
) -> None
Skip the current test on a Spark Connect session — for tests that
reach JVM-only APIs (sparkContext, rdd) absent in Connect.
df ¶
Shorthand for self.spark.createDataFrame(data, schema).
arrow_to_spark ¶
Convert a pyarrow.Table to a Spark DataFrame via pandas.
Uses Arrow-backed pandas for zero-copy-ish transfer. Good enough for tests; don't use this on gigabyte-scale data.
spark_to_arrow ¶
Materialise a Spark DataFrame as a pyarrow.Table.
assertDataFrameEqual ¶
assertDataFrameEqual(
actual: "DataFrame",
expected: "DataFrame | pa.Table | list[dict[str, Any]]",
*,
ordered: bool = False,
check_schema: bool = True
) -> None
Assert two DataFrames are equal.
Parameters¶
actual : DataFrame The DataFrame produced by the code under test. expected : DataFrame | pa.Table | list[dict] The reference value. Accepts a Spark DataFrame, a pyarrow Table, or a list of row dicts (useful for inline literals). ordered : bool, default False If False, both sides are sorted by all columns before compare. check_schema : bool, default True If True, schemas (names + types) must match exactly.
assertSchemaEqual ¶
Assert a DataFrame has exactly the given (name, dtype) fields.
dtype may be a pyspark.sql.types.DataType instance or its
simpleString form ("int", "string", "array<long>", ...).
spark_tmp_path ¶
Per-test scratch path — just an alias for clarity in Spark tests.