This is an automated email from the ASF dual-hosted git repository.
zhengruifeng pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.x by this push:
new cece1ec224d1 [SPARK-58081][INFRA] Document Python test base classes in
AGENTS.md
cece1ec224d1 is described below
commit cece1ec224d13db9162a848ee4ee43d75443de99
Author: Ruifeng Zheng <[email protected]>
AuthorDate: Fri Jul 10 14:18:54 2026 +0800
[SPARK-58081][INFRA] Document Python test base classes in AGENTS.md
### What changes were proposed in this pull request?
Add a `## Python Test Base Classes` section to `AGENTS.md` (`CLAUDE.md` is
a symlink to it), right after the existing `## Scala Test Base Classes` section
and mirroring its structure: a short intro, an ASCII "adds capability on top of
the previous" ladder, and a scope/base/notes table.
It covers:
- the classic ladder `unittest.TestCase` -> `PySparkBaseTestCase` ->
`ReusedPySparkTestCase` -> `ReusedSQLTestCase` -> `PandasOnSparkTestCase`,
including the fresh-vs-shared `SparkContext` distinction (`PySparkTestCase` vs
`ReusedPySparkTestCase`);
- a `### Spark Connect test bases` subsection for `PlanOnlyTestFixture`,
`ReusedConnectTestCase`, and `ReusedMixedTestCase`, noting the
`should_test_connect` auto-skip;
- a `### Mixins and helpers` subsection for `SQLTestUtils`,
`PySparkErrorTestUtils`, the standalone `assertDataFrameEqual` /
`assertSchemaEqual`, and the off-ladder domain bases (`SparkSessionTestCase`,
`MLlibTestCase`, `PySparkStreamingTestCase`).
### Why are the changes needed?
`AGENTS.md` already guides contributors (and AI agents) to pick the lowest
suitable Scala test base class, but has no equivalent for PySpark. The Python
test bases under `python/pyspark/testing/` form a similar ladder that is easy
to get wrong (e.g. subclassing a heavier base than needed, or missing the
Connect-specific bases). Documenting them fills the adjacent gap.
### Does this PR introduce _any_ user-facing change?
No. Documentation only.
### How was this patch tested?
No code changes. Verified the described class hierarchy and capabilities
against the sources under `python/pyspark/testing/` (`utils.py`, `sqlutils.py`,
`connectutils.py`, `pandasutils.py`, `mlutils.py`, `mllibutils.py`,
`streamingutils.py`), and spot-checked that only em-dashes (U+2014) were added
as non-ASCII, consistent with the rest of the file.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.8)
Closes #57154 from zhengruifeng/python-test-base-classes-dev1.
Authored-by: Ruifeng Zheng <[email protected]>
Signed-off-by: Ruifeng Zheng <[email protected]>
(cherry picked from commit 10b463c695aa749cd8ba8cdcf5cc4f1eca51927c)
Signed-off-by: Ruifeng Zheng <[email protected]>
---
AGENTS.md | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/AGENTS.md b/AGENTS.md
index 7f038fd44a06..2d481b6a6bc1 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -47,6 +47,47 @@ When writing a new Scala test suite, pick the lowest base
class that provides wh
| `SharedSparkSession` | `sql/core` | Already extends `QueryTest` for
historical reasons, but still mix in `QueryTest` explicitly, e.g. `class X
extends QueryTest with SharedSparkSession`. Default for tests under `sql/core`.
|
| `TestHiveSingleton` | `sql/hive` | Mixed in alongside `QueryTest`, e.g.
`class X extends QueryTest with TestHiveSingleton`. Used by tests under
`sql/hive`. |
+## Python Test Base Classes
+
+PySpark tests use the stdlib `unittest` framework: every suite subclasses
`unittest.TestCase` (run via `python/run-tests`, see below). As with Scala,
pick the lowest base that provides what the test actually needs. The bases live
under `python/pyspark/testing/` and each adds capability on top of the previous:
+
+ unittest.TestCase (stdlib)
+ <- PySparkBaseTestCase
(pyspark.testing.utils)
+ <- ReusedPySparkTestCase
(pyspark.testing.utils)
+ <- ReusedSQLTestCase
(pyspark.testing.sqlutils)
+ <- PandasOnSparkTestCase
(pyspark.testing.pandasutils)
+
+| Test scope | Base | Notes |
+|------------|------|-------|
+| Plain Python — no Spark | `unittest.TestCase` | Use the stdlib base
directly. `PySparkBaseTestCase` is the same thing plus a SIGTERM fault-handler
dump enabled when `PYSPARK_TEST_TIMEOUT` is set; subclass it only when you want
that. |
+| RDD / `SparkContext` — no `SparkSession` | `PySparkTestCase` (fresh) or
`ReusedPySparkTestCase` (shared) | Both create a `SparkContext("local[4]")`.
`PySparkTestCase` makes a new one per test (isolation); `ReusedPySparkTestCase`
shares one per class (faster, the usual choice) and adds `quiet()` and an
overridable `conf()` / `master()`. |
+| SQL / DataFrame — needs a `SparkSession` | `ReusedSQLTestCase` | The
workhorse for classic tests under `python/pyspark/sql`. Adds a shared
`cls.spark`, sample `cls.df` / `cls.testData`, and mixes in `SQLTestUtils` +
`PySparkErrorTestUtils`. Classic (non-Connect) mode. |
+| pandas API on Spark | `PandasOnSparkTestCase` | Extends `ReusedSQLTestCase`
with Arrow enabled and pandas-on-Spark assertions (`PandasOnSparkTestUtils`);
`ComparisonTestBase` builds on it. |
+
+### Spark Connect test bases
+
+Spark Connect suites live in `pyspark.testing.connectutils` and are
auto-skipped (via `should_test_connect`) when Connect dependencies are missing:
+
+ PySparkBaseTestCase
(pyspark.testing.utils)
+ <- PlanOnlyTestFixture
(pyspark.testing.connectutils)
+ <- ReusedConnectTestCase
(pyspark.testing.connectutils)
+ <- ReusedMixedTestCase
(pyspark.testing.connectutils)
+
+| Test scope | Base | Notes |
+|------------|------|-------|
+| Plan / proto construction — no server | `PlanOnlyTestFixture` | Uses a
`MockRemoteSession`; builds and inspects plans without a running Connect
server. For proto / plan-shape assertions. |
+| Connect DataFrame — real session | `ReusedConnectTestCase` | The Connect
analog of `ReusedSQLTestCase`; starts a session via `.remote(...)` (honoring
`SPARK_CONNECT_TESTING_REMOTE`, default `local[4]`). Mixes in `SQLTestUtils` +
`PySparkErrorTestUtils`. |
+| Classic + Connect side by side | `ReusedMixedTestCase` | Extends
`ReusedConnectTestCase`. For directly comparing classic vs Connect: it exposes
a classic `self.spark` and a Connect `self.connect` so a test can run the same
operation on each and assert they agree (`compare_by_show`, `both_conf`).
Requires JVM access. |
+
+### Mixins and helpers
+
+These are combined with a base above rather than used on their own:
+
+- `SQLTestUtils` — context managers `sql_conf`, `table`, `temp_view`, `view`,
`database`, `function`, `temp_func`, `temp_env`; assumes `self.spark`. Already
mixed into `ReusedSQLTestCase` and `ReusedConnectTestCase`.
+- `PySparkErrorTestUtils` — `check_error(...)` to assert on a
`PySparkException`'s error class and message parameters. The Python counterpart
of Scala's `checkError`.
+- `assertDataFrameEqual` / `assertSchemaEqual` (public API, from
`pyspark.testing`) — standalone assertion functions that work in any test, no
particular base class required.
+- Domain bases outside the main ladder: `SparkSessionTestCase`
(`pyspark.testing.mlutils`, for `ml`), `MLlibTestCase`
(`pyspark.testing.mllibutils`), and `PySparkStreamingTestCase`
(`pyspark.testing.streamingutils`, DStreams).
+
## Build and Test
Build and tests can take a long time. If the user explicitly asked to run
tests, run them. Otherwise (you are running tests on your own to verify a
change), first ask the user if they have more changes to make.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]