Skip to main content
Archived

This project has been archived by its maintainers, and is no longer receiving any updates.

sparkless-testing

Easy testing with sparkless or PySpark on demand

A Python package that simplifies running tests with either sparkless (mock) or PySpark (real) engines on demand. Write tests once, run them with either engine seamlessly.

Why sparkless-testing?

  • 🚀 Fast Mock Testing: Use sparkless for lightning-fast unit tests without JVM overhead
  • 🔄 Real Engine Validation: Test against real PySpark to catch integration issues
  • 🎯 Single Test Suite: Write tests once, run with both engines automatically
  • 🔧 Zero Configuration: Automatic engine detection and session management
  • ⚡ Parallel Ready: Optimized for parallel test execution with pytest-xdist

Features

  • Automatic Engine Detection - Automatically detects and configures available engines
  • Pytest Fixtures - Ready-to-use fixtures for both engines
  • Session Management - Automatic session creation, cleanup, and isolation
  • Test Utilities - Helpers for common test patterns
  • Parametrization Support - Run tests with both engines automatically
  • Parallel Testing Support - Optimized for parallel test execution with pytest-xdist

Installation

pip install sparkless-testing

For specific engines:

# Install with mock engine (sparkless)
pip install sparkless-testing[mock]

# Install with PySpark engine
pip install sparkless-testing[pyspark]

# Install with all engines
pip install sparkless-testing[dev]

Quick Start

Basic Usage

import pytest
from sparkless_testing import pytest_fixtures

# Use the spark_session fixture
def test_my_function(spark_session):
    df = spark_session.createDataFrame([{"id": 1, "name": "Alice"}])
    assert df.count() == 1
    assert df.collect()[0]["name"] == "Alice"

Explicit Engine Selection

def test_mock_only(mock_spark_session):
    # Only runs with sparkless
    df = mock_spark_session.createDataFrame([{"id": 1}])
    assert df.count() == 1

def test_pyspark_only(pyspark_session):
    # Only runs with PySpark
    df = pyspark_session.createDataFrame([{"id": 1}])
    assert df.count() == 1

Using Functions and Types

def test_with_functions(spark_session, spark_functions, spark_types):
    F = spark_functions
    Types = spark_types
    
    df = spark_session.createDataFrame([{"name": "Alice"}])
    result = df.select(F.upper(F.col("name"))).collect()
    assert result[0][0] == "ALICE"

Note: Always use spark_functions together with spark_session. PySpark functions like F.col() require an active SparkContext, which is provided by the spark_session fixture.

Parametrized Tests (Both Engines)

You can run tests with both engines in two ways:

Option 1: Using the decorator

@pytest.mark.parametrize_engines
def test_both_engines(spark_session):
    # Runs with both engines automatically
    df = spark_session.createDataFrame([{"id": 1}])
    assert df.count() == 1

Option 2: Using the both_engines fixture

def test_both_engines(both_engines):
    spark, engine_type = both_engines
    # Runs with both engines automatically
    df = spark.createDataFrame([{"id": 1}])
    assert df.count() == 1
    # engine_type will be EngineType.MOCK or EngineType.PYSPARK

Environment-Based Configuration

# Run tests with mock engine
SPARK_MODE=mock pytest tests/

# Run tests with PySpark engine
SPARK_MODE=pyspark pytest tests/

# Auto-detect (default)
SPARK_MODE=auto pytest tests/

Pytest Markers

@pytest.mark.spark_engine("mock")
def test_mock_specific(spark_session):
    # Forces mock engine
    pass

@pytest.mark.spark_engine("pyspark")
def test_pyspark_specific(spark_session):
    # Forces PySpark engine
    pass

Configuration

Environment Variables

  • SPARK_MODE: mock, pyspark, or auto (default: auto)
  • SPARK_TEST_WAREHOUSE_DIR: Custom warehouse directory for PySpark tests
  • SPARK_TEST_APP_NAME: Custom app name prefix (default: sparkless-testing)

Programmatic Configuration

from sparkless_testing import (
    EngineType,
    SessionConfig,
    auto_configure_engine,
    create_session,
    create_pyspark_session,
)

# Auto-configure based on availability
auto_configure_engine()

# Or specify preferred engine
auto_configure_engine(EngineType.MOCK)

# Create session manually
spark = create_session(engine_type=EngineType.MOCK)

# Create session with custom configuration
config = SessionConfig(
    app_name="my-test",
    shuffle_partitions=2,
)
spark = create_pyspark_session(config=config)
# ... use spark ...
spark.stop()

API Reference

Engine Configuration

  • EngineType: Enum for engine types (MOCK, PYSPARK, AUTO)
  • configure_engine(): Manually configure engine components
  • get_engine(): Get current engine configuration
  • detect_available_engines(): Check which engines are installed
  • auto_configure_engine(): Automatically configure based on availability

Session Factory

  • create_mock_session(): Create sparkless session
  • create_pyspark_session(): Create PySpark session
  • create_session(): Factory function that creates appropriate session
  • SessionConfig: Configuration dataclass for session creation
    • app_name: Application name for the Spark session
    • warehouse_dir: Custom warehouse directory (optional)
    • enable_ui: Enable Spark UI (default: False)
    • shuffle_partitions: Number of shuffle partitions (default: 1)
    • parallelism: Default parallelism (default: 1)
    • adaptive_enabled: Enable adaptive query execution (default: False)

Test Utilities

  • detect_spark_type(spark): Detect if session is PySpark or mock
  • create_test_dataframe(spark, data, schema): Compatibility wrapper for DataFrame creation
  • is_dataframe_like(obj): Check if object is DataFrame-like

Pytest Fixtures

  • spark_session: Main fixture (auto-detects engine based on SPARK_MODE or markers)
  • mock_spark_session: Explicitly use sparkless (mock) engine
  • pyspark_session: Explicitly use PySpark (real) engine
  • both_engines: Fixture that yields (spark, engine_type) for both engines (parametrized)
  • spark_functions: Functions module (F) for current engine
  • spark_types: Types module for current engine
  • spark_engine_type: Current engine type as string ("mock" or "pyspark")

Advanced Usage

Custom Session Configuration

from sparkless_testing import SessionConfig, create_pyspark_session

def test_with_custom_config():
    config = SessionConfig(
        app_name="custom-test",
        warehouse_dir="/tmp/my-warehouse",
        enable_ui=True,
        shuffle_partitions=4,
        parallelism=4,
    )
    spark = create_pyspark_session(config=config)
    # ... your test code ...
    spark.stop()

Parallel Test Execution

# Run tests in parallel with pytest-xdist
pytest -n 2  # Use 2 workers
pytest -n auto  # Auto-detect worker count

Best Practices for Parallel Testing:

  • Mock (sparkless) tests can run with high parallelism (-n 10+)
  • PySpark tests work best with fewer workers (-n 2 or -n 4)
  • Each test gets a unique session name and warehouse directory

Using Test Utilities

from sparkless_testing.utils import create_test_dataframe, detect_spark_type

def test_with_utilities(spark_session):
    # Create DataFrame with schema handling
    data = [("Alice", 25), ("Bob", 30)]
    schema = ["name", "age"]
    df = create_test_dataframe(spark_session, data, schema)
    
    # Detect engine type
    engine = detect_spark_type(spark_session)
    assert engine in ("mock", "pyspark")

Examples

Complete Test Example

import pytest
from sparkless_testing.pytest_fixtures import spark_session, spark_functions

def test_data_transformation(spark_session, spark_functions):
    F = spark_functions
    
    # Create test data
    data = [
        {"id": 1, "name": "Alice", "age": 25},
        {"id": 2, "name": "Bob", "age": 30},
    ]
    df = spark_session.createDataFrame(data)
    
    # Transform data
    result = (
        df.filter(F.col("age") > 25)
        .select("name", "age")
        .collect()
    )
    
    # Assertions
    assert len(result) == 1
    assert result[0]["name"] == "Bob"

Using with Both Engines

@pytest.mark.parametrize_engines
def test_compatibility(spark_session, spark_engine_type):
    # This test runs with both engines
    df = spark_session.createDataFrame([{"value": 42}])
    
    # Engine-specific logic if needed
    if spark_engine_type == "mock":
        # Mock-specific assertions
        pass
    else:
        # PySpark-specific assertions
        pass
    
    assert df.count() == 1

Troubleshooting

Tests Hang with Parallel Execution

If tests hang when using pytest -n, try:

  • Use fewer workers: pytest -n 2 instead of pytest -n 10
  • Run PySpark tests sequentially: pytest -n 0 for PySpark-only tests
  • Use mock engine for faster parallel execution: SPARK_MODE=mock pytest -n 10

SparkContext Errors

Always use spark_functions together with spark_session:

def test_correct(spark_session, spark_functions):  # ✅ Correct
    F = spark_functions
    df = spark_session.createDataFrame([{"x": 1}])
    result = df.select(F.col("x")).collect()

def test_incorrect(spark_functions):  # ❌ May fail with PySpark
    F = spark_functions
    # Missing active SparkContext

Migration Guide

From Manual Engine Switching

Before:

import os
from sparkless import SparkSession as MockSparkSession
from pyspark.sql import SparkSession as PySparkSession

def test_my_function():
    if os.environ.get("SPARK_MODE") == "mock":
        spark = MockSparkSession("test")
    else:
        spark = PySparkSession.builder.appName("test").getOrCreate()
    # ... test code ...
    spark.stop()

After:

from sparkless_testing.pytest_fixtures import spark_session

def test_my_function(spark_session):
    # ... test code ...
    # spark_session is automatically cleaned up

Requirements

  • Python 3.9+
  • pytest 7.0+

Optional:

  • sparkless>=3.19.0 (for mock engine)
  • pyspark>=3.5.0 (for real engine)
  • pytest-xdist (for parallel test execution)

License

MIT License - see LICENSE file for details.


Made with ❤️ for the data engineering community

Metadata

Release files for sparkless-testing 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sparkless-testing 0.2.0
File Size Uploaded
sparkless_testing-0.2.0.tar.gz 30.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sparkless-testing 0.2.0
File Interpreter ABI Platform
sparkless_testing-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 50.1 kB

Release files / sparkless_testing-0.2.0.tar.gz

Download URL sparkless_testing-0.2.0.tar.gz
Size 30.4 kB
Tags Source
SHA-256 checksum
How to use checksums
3a59320dd810d12559780df608e1329d4b75d1c970221d86ec7a06f419cafb20
BLAKE2b-256 checksum
How to use checksums
64d6106610c9d7f997ad3cf77040008b9461b7949606543f3f29ed327162ae26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release files / sparkless_testing-0.2.0-py3-none-any.whl

Download URL sparkless_testing-0.2.0-py3-none-any.whl
Size 19.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
abebd0ded3eb6d68a8df96cd664cdf04c57bc6f0e54fd6cb087969479b707a0f
BLAKE2b-256 checksum
How to use checksums
8cf66d1d4199a44063dc18a7717c0398e7c0e2db621f4710c7929015d1cb08d3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page