sparkless-testing 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sparkless_testing-0.1.0/LICENSE +22 -0
- sparkless_testing-0.1.0/PKG-INFO +445 -0
- sparkless_testing-0.1.0/README.md +404 -0
- sparkless_testing-0.1.0/pyproject.toml +141 -0
- sparkless_testing-0.1.0/setup.cfg +4 -0
- sparkless_testing-0.1.0/sparkless_testing/__init__.py +87 -0
- sparkless_testing-0.1.0/sparkless_testing/conftest.py +39 -0
- sparkless_testing-0.1.0/sparkless_testing/engine.py +306 -0
- sparkless_testing-0.1.0/sparkless_testing/parametrize.py +150 -0
- sparkless_testing-0.1.0/sparkless_testing/protocols.py +145 -0
- sparkless_testing-0.1.0/sparkless_testing/py.typed +0 -0
- sparkless_testing-0.1.0/sparkless_testing/pytest_fixtures.py +235 -0
- sparkless_testing-0.1.0/sparkless_testing/session.py +262 -0
- sparkless_testing-0.1.0/sparkless_testing/utils.py +209 -0
- sparkless_testing-0.1.0/sparkless_testing.egg-info/PKG-INFO +445 -0
- sparkless_testing-0.1.0/sparkless_testing.egg-info/SOURCES.txt +22 -0
- sparkless_testing-0.1.0/sparkless_testing.egg-info/dependency_links.txt +1 -0
- sparkless_testing-0.1.0/sparkless_testing.egg-info/requires.txt +19 -0
- sparkless_testing-0.1.0/sparkless_testing.egg-info/top_level.txt +1 -0
- sparkless_testing-0.1.0/tests/test_engine.py +389 -0
- sparkless_testing-0.1.0/tests/test_fixtures.py +344 -0
- sparkless_testing-0.1.0/tests/test_parametrize.py +150 -0
- sparkless_testing-0.1.0/tests/test_session.py +747 -0
- sparkless_testing-0.1.0/tests/test_utils.py +428 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Odos Matthews
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sparkless-testing
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Easy testing with sparkless or PySpark on demand
|
|
5
|
+
Author-email: Odos Matthews <odosmatthews@gmail.com>
|
|
6
|
+
Maintainer-email: Odos Matthews <odosmatthews@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/eddiethedean/sparkless-testing
|
|
9
|
+
Project-URL: Repository, https://github.com/eddiethedean/sparkless-testing
|
|
10
|
+
Project-URL: Issues, https://github.com/eddiethedean/sparkless-testing/issues
|
|
11
|
+
Keywords: spark,pyspark,testing,pytest,sparkless,data-engineering,unit-testing,fixtures
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: pytest>=7.0.0
|
|
26
|
+
Provides-Extra: mock
|
|
27
|
+
Requires-Dist: sparkless>=3.19.0; extra == "mock"
|
|
28
|
+
Provides-Extra: pyspark
|
|
29
|
+
Requires-Dist: pyspark<3.6.0,>=3.5.0; extra == "pyspark"
|
|
30
|
+
Provides-Extra: delta
|
|
31
|
+
Requires-Dist: delta-spark<4.0.0,>=3.0.0; extra == "delta"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
37
|
+
Requires-Dist: sparkless>=3.19.0; extra == "dev"
|
|
38
|
+
Requires-Dist: pyspark<3.6.0,>=3.5.0; extra == "dev"
|
|
39
|
+
Requires-Dist: delta-spark<4.0.0,>=3.0.0; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# sparkless-testing
|
|
43
|
+
|
|
44
|
+
> **Easy testing with sparkless or PySpark on demand**
|
|
45
|
+
|
|
46
|
+
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.
|
|
47
|
+
|
|
48
|
+
## Why sparkless-testing?
|
|
49
|
+
|
|
50
|
+
- 🚀 **Fast Mock Testing**: Use `sparkless` for lightning-fast unit tests without JVM overhead
|
|
51
|
+
- 🔄 **Real Engine Validation**: Test against real PySpark to catch integration issues
|
|
52
|
+
- 🎯 **Single Test Suite**: Write tests once, run with both engines automatically
|
|
53
|
+
- 🔧 **Zero Configuration**: Automatic engine detection and session management
|
|
54
|
+
- ⚡ **Parallel Ready**: Optimized for parallel test execution with pytest-xdist
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
- **Automatic Engine Detection** - Automatically detects and configures available engines
|
|
59
|
+
- **Pytest Fixtures** - Ready-to-use fixtures for both engines
|
|
60
|
+
- **Session Management** - Automatic session creation, cleanup, and isolation
|
|
61
|
+
- **Test Utilities** - Helpers for common test patterns
|
|
62
|
+
- **Parametrization Support** - Run tests with both engines automatically
|
|
63
|
+
- **Parallel Testing Support** - Optimized for parallel test execution with pytest-xdist
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install sparkless-testing
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For specific engines:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Install with mock engine (sparkless)
|
|
75
|
+
pip install sparkless-testing[mock]
|
|
76
|
+
|
|
77
|
+
# Install with PySpark engine
|
|
78
|
+
pip install sparkless-testing[pyspark]
|
|
79
|
+
|
|
80
|
+
# Install with Delta Lake support
|
|
81
|
+
pip install sparkless-testing[delta]
|
|
82
|
+
|
|
83
|
+
# Install with all engines
|
|
84
|
+
pip install sparkless-testing[dev]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Quick Start
|
|
88
|
+
|
|
89
|
+
### Basic Usage
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import pytest
|
|
93
|
+
from sparkless_testing import pytest_fixtures
|
|
94
|
+
|
|
95
|
+
# Use the spark_session fixture
|
|
96
|
+
def test_my_function(spark_session):
|
|
97
|
+
df = spark_session.createDataFrame([{"id": 1, "name": "Alice"}])
|
|
98
|
+
assert df.count() == 1
|
|
99
|
+
assert df.collect()[0]["name"] == "Alice"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Explicit Engine Selection
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
def test_mock_only(mock_spark_session):
|
|
106
|
+
# Only runs with sparkless
|
|
107
|
+
df = mock_spark_session.createDataFrame([{"id": 1}])
|
|
108
|
+
assert df.count() == 1
|
|
109
|
+
|
|
110
|
+
def test_pyspark_only(pyspark_session):
|
|
111
|
+
# Only runs with PySpark
|
|
112
|
+
df = pyspark_session.createDataFrame([{"id": 1}])
|
|
113
|
+
assert df.count() == 1
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Using Functions and Types
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
def test_with_functions(spark_session, spark_functions, spark_types):
|
|
120
|
+
F = spark_functions
|
|
121
|
+
Types = spark_types
|
|
122
|
+
|
|
123
|
+
df = spark_session.createDataFrame([{"name": "Alice"}])
|
|
124
|
+
result = df.select(F.upper(F.col("name"))).collect()
|
|
125
|
+
assert result[0][0] == "ALICE"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**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.
|
|
129
|
+
|
|
130
|
+
### Parametrized Tests (Both Engines)
|
|
131
|
+
|
|
132
|
+
You can run tests with both engines in two ways:
|
|
133
|
+
|
|
134
|
+
**Option 1: Using the decorator**
|
|
135
|
+
```python
|
|
136
|
+
@pytest.mark.parametrize_engines
|
|
137
|
+
def test_both_engines(spark_session):
|
|
138
|
+
# Runs with both engines automatically
|
|
139
|
+
df = spark_session.createDataFrame([{"id": 1}])
|
|
140
|
+
assert df.count() == 1
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Option 2: Using the `both_engines` fixture**
|
|
144
|
+
```python
|
|
145
|
+
def test_both_engines(both_engines):
|
|
146
|
+
spark, engine_type = both_engines
|
|
147
|
+
# Runs with both engines automatically
|
|
148
|
+
df = spark.createDataFrame([{"id": 1}])
|
|
149
|
+
assert df.count() == 1
|
|
150
|
+
# engine_type will be EngineType.MOCK or EngineType.PYSPARK
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Environment-Based Configuration
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Run tests with mock engine
|
|
157
|
+
SPARK_MODE=mock pytest tests/
|
|
158
|
+
|
|
159
|
+
# Run tests with PySpark engine
|
|
160
|
+
SPARK_MODE=pyspark pytest tests/
|
|
161
|
+
|
|
162
|
+
# Auto-detect (default)
|
|
163
|
+
SPARK_MODE=auto pytest tests/
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Pytest Markers
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
@pytest.mark.spark_engine("mock")
|
|
170
|
+
def test_mock_specific(spark_session):
|
|
171
|
+
# Forces mock engine
|
|
172
|
+
pass
|
|
173
|
+
|
|
174
|
+
@pytest.mark.spark_engine("pyspark")
|
|
175
|
+
def test_pyspark_specific(spark_session):
|
|
176
|
+
# Forces PySpark engine
|
|
177
|
+
pass
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Configuration
|
|
181
|
+
|
|
182
|
+
### Environment Variables
|
|
183
|
+
|
|
184
|
+
- `SPARK_MODE`: `mock`, `pyspark`, or `auto` (default: `auto`)
|
|
185
|
+
- `SPARK_TEST_WAREHOUSE_DIR`: Custom warehouse directory for PySpark tests
|
|
186
|
+
- `SPARK_TEST_APP_NAME`: Custom app name prefix (default: `sparkless-testing`)
|
|
187
|
+
|
|
188
|
+
### Programmatic Configuration
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
from sparkless_testing import (
|
|
192
|
+
EngineType,
|
|
193
|
+
SessionConfig,
|
|
194
|
+
auto_configure_engine,
|
|
195
|
+
create_session,
|
|
196
|
+
create_pyspark_session,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Auto-configure based on availability
|
|
200
|
+
auto_configure_engine()
|
|
201
|
+
|
|
202
|
+
# Or specify preferred engine
|
|
203
|
+
auto_configure_engine(EngineType.MOCK)
|
|
204
|
+
|
|
205
|
+
# Create session manually
|
|
206
|
+
spark = create_session(engine_type=EngineType.MOCK)
|
|
207
|
+
|
|
208
|
+
# Create session with custom configuration
|
|
209
|
+
config = SessionConfig(
|
|
210
|
+
app_name="my-test",
|
|
211
|
+
enable_delta=True,
|
|
212
|
+
shuffle_partitions=2,
|
|
213
|
+
)
|
|
214
|
+
spark = create_pyspark_session(config=config)
|
|
215
|
+
# ... use spark ...
|
|
216
|
+
spark.stop()
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## API Reference
|
|
220
|
+
|
|
221
|
+
### Engine Configuration
|
|
222
|
+
|
|
223
|
+
- `EngineType`: Enum for engine types (`MOCK`, `PYSPARK`, `AUTO`)
|
|
224
|
+
- `configure_engine()`: Manually configure engine components
|
|
225
|
+
- `get_engine()`: Get current engine configuration
|
|
226
|
+
- `detect_available_engines()`: Check which engines are installed
|
|
227
|
+
- `auto_configure_engine()`: Automatically configure based on availability
|
|
228
|
+
|
|
229
|
+
### Session Factory
|
|
230
|
+
|
|
231
|
+
- `create_mock_session()`: Create sparkless session
|
|
232
|
+
- `create_pyspark_session()`: Create PySpark session
|
|
233
|
+
- `create_session()`: Factory function that creates appropriate session
|
|
234
|
+
- `SessionConfig`: Configuration dataclass for session creation
|
|
235
|
+
- `app_name`: Application name for the Spark session
|
|
236
|
+
- `warehouse_dir`: Custom warehouse directory (optional)
|
|
237
|
+
- `enable_delta`: Enable Delta Lake support (default: `True` for programmatic use, `False` in test fixtures)
|
|
238
|
+
- `enable_ui`: Enable Spark UI (default: `False`)
|
|
239
|
+
- `shuffle_partitions`: Number of shuffle partitions (default: `1`)
|
|
240
|
+
- `parallelism`: Default parallelism (default: `1`)
|
|
241
|
+
- `adaptive_enabled`: Enable adaptive query execution (default: `False`)
|
|
242
|
+
|
|
243
|
+
### Test Utilities
|
|
244
|
+
|
|
245
|
+
- `detect_spark_type(spark)`: Detect if session is PySpark or mock
|
|
246
|
+
- `create_test_dataframe(spark, data, schema)`: Compatibility wrapper for DataFrame creation
|
|
247
|
+
- `is_dataframe_like(obj)`: Check if object is DataFrame-like
|
|
248
|
+
|
|
249
|
+
### Pytest Fixtures
|
|
250
|
+
|
|
251
|
+
- `spark_session`: Main fixture (auto-detects engine based on `SPARK_MODE` or markers)
|
|
252
|
+
- `mock_spark_session`: Explicitly use sparkless (mock) engine
|
|
253
|
+
- `pyspark_session`: Explicitly use PySpark (real) engine
|
|
254
|
+
- `both_engines`: Fixture that yields `(spark, engine_type)` for both engines (parametrized)
|
|
255
|
+
- `spark_functions`: Functions module (F) for current engine
|
|
256
|
+
- `spark_types`: Types module for current engine
|
|
257
|
+
- `spark_engine_type`: Current engine type as string (`"mock"` or `"pyspark"`)
|
|
258
|
+
|
|
259
|
+
**Note**: Test fixtures automatically disable Delta Lake for parallel testing compatibility. To use Delta Lake, create sessions programmatically with `enable_delta=True` in `SessionConfig`.
|
|
260
|
+
|
|
261
|
+
## Advanced Usage
|
|
262
|
+
|
|
263
|
+
### Custom Session Configuration
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from sparkless_testing import SessionConfig, create_pyspark_session
|
|
267
|
+
|
|
268
|
+
def test_with_custom_config():
|
|
269
|
+
config = SessionConfig(
|
|
270
|
+
app_name="custom-test",
|
|
271
|
+
warehouse_dir="/tmp/my-warehouse",
|
|
272
|
+
enable_delta=True,
|
|
273
|
+
enable_ui=True,
|
|
274
|
+
shuffle_partitions=4,
|
|
275
|
+
parallelism=4,
|
|
276
|
+
)
|
|
277
|
+
spark = create_pyspark_session(config=config)
|
|
278
|
+
# ... your test code ...
|
|
279
|
+
spark.stop()
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Parallel Test Execution
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# Run tests in parallel with pytest-xdist
|
|
286
|
+
pytest -n 2 # Use 2 workers
|
|
287
|
+
pytest -n auto # Auto-detect worker count
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Best Practices for Parallel Testing:**
|
|
291
|
+
- Mock (sparkless) tests can run with high parallelism (`-n 10+`)
|
|
292
|
+
- PySpark tests work best with fewer workers (`-n 2` or `-n 4`)
|
|
293
|
+
- Test fixtures automatically disable Delta Lake to avoid conflicts
|
|
294
|
+
- Each test gets a unique session name and warehouse directory
|
|
295
|
+
|
|
296
|
+
### Using Test Utilities
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
from sparkless_testing.utils import create_test_dataframe, detect_spark_type
|
|
300
|
+
|
|
301
|
+
def test_with_utilities(spark_session):
|
|
302
|
+
# Create DataFrame with schema handling
|
|
303
|
+
data = [("Alice", 25), ("Bob", 30)]
|
|
304
|
+
schema = ["name", "age"]
|
|
305
|
+
df = create_test_dataframe(spark_session, data, schema)
|
|
306
|
+
|
|
307
|
+
# Detect engine type
|
|
308
|
+
engine = detect_spark_type(spark_session)
|
|
309
|
+
assert engine in ("mock", "pyspark")
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Examples
|
|
313
|
+
|
|
314
|
+
### Complete Test Example
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
import pytest
|
|
318
|
+
from sparkless_testing.pytest_fixtures import spark_session, spark_functions
|
|
319
|
+
|
|
320
|
+
def test_data_transformation(spark_session, spark_functions):
|
|
321
|
+
F = spark_functions
|
|
322
|
+
|
|
323
|
+
# Create test data
|
|
324
|
+
data = [
|
|
325
|
+
{"id": 1, "name": "Alice", "age": 25},
|
|
326
|
+
{"id": 2, "name": "Bob", "age": 30},
|
|
327
|
+
]
|
|
328
|
+
df = spark_session.createDataFrame(data)
|
|
329
|
+
|
|
330
|
+
# Transform data
|
|
331
|
+
result = (
|
|
332
|
+
df.filter(F.col("age") > 25)
|
|
333
|
+
.select("name", "age")
|
|
334
|
+
.collect()
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
# Assertions
|
|
338
|
+
assert len(result) == 1
|
|
339
|
+
assert result[0]["name"] == "Bob"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Using with Both Engines
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
@pytest.mark.parametrize_engines
|
|
346
|
+
def test_compatibility(spark_session, spark_engine_type):
|
|
347
|
+
# This test runs with both engines
|
|
348
|
+
df = spark_session.createDataFrame([{"value": 42}])
|
|
349
|
+
|
|
350
|
+
# Engine-specific logic if needed
|
|
351
|
+
if spark_engine_type == "mock":
|
|
352
|
+
# Mock-specific assertions
|
|
353
|
+
pass
|
|
354
|
+
else:
|
|
355
|
+
# PySpark-specific assertions
|
|
356
|
+
pass
|
|
357
|
+
|
|
358
|
+
assert df.count() == 1
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Troubleshooting
|
|
362
|
+
|
|
363
|
+
### Tests Hang with Parallel Execution
|
|
364
|
+
|
|
365
|
+
If tests hang when using `pytest -n`, try:
|
|
366
|
+
- Use fewer workers: `pytest -n 2` instead of `pytest -n 10`
|
|
367
|
+
- Run PySpark tests sequentially: `pytest -n 0` for PySpark-only tests
|
|
368
|
+
- Use mock engine for faster parallel execution: `SPARK_MODE=mock pytest -n 10`
|
|
369
|
+
|
|
370
|
+
### Delta Lake Not Available
|
|
371
|
+
|
|
372
|
+
Delta Lake is disabled by default in test fixtures. To enable:
|
|
373
|
+
```python
|
|
374
|
+
from sparkless_testing import SessionConfig, create_pyspark_session
|
|
375
|
+
|
|
376
|
+
config = SessionConfig(app_name="test", enable_delta=True)
|
|
377
|
+
spark = create_pyspark_session(config=config)
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### SparkContext Errors
|
|
381
|
+
|
|
382
|
+
Always use `spark_functions` together with `spark_session`:
|
|
383
|
+
```python
|
|
384
|
+
def test_correct(spark_session, spark_functions): # ✅ Correct
|
|
385
|
+
F = spark_functions
|
|
386
|
+
df = spark_session.createDataFrame([{"x": 1}])
|
|
387
|
+
result = df.select(F.col("x")).collect()
|
|
388
|
+
|
|
389
|
+
def test_incorrect(spark_functions): # ❌ May fail with PySpark
|
|
390
|
+
F = spark_functions
|
|
391
|
+
# Missing active SparkContext
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Migration Guide
|
|
395
|
+
|
|
396
|
+
### From Manual Engine Switching
|
|
397
|
+
|
|
398
|
+
**Before:**
|
|
399
|
+
```python
|
|
400
|
+
import os
|
|
401
|
+
from sparkless import SparkSession as MockSparkSession
|
|
402
|
+
from pyspark.sql import SparkSession as PySparkSession
|
|
403
|
+
|
|
404
|
+
def test_my_function():
|
|
405
|
+
if os.environ.get("SPARK_MODE") == "mock":
|
|
406
|
+
spark = MockSparkSession("test")
|
|
407
|
+
else:
|
|
408
|
+
spark = PySparkSession.builder.appName("test").getOrCreate()
|
|
409
|
+
# ... test code ...
|
|
410
|
+
spark.stop()
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**After:**
|
|
414
|
+
```python
|
|
415
|
+
from sparkless_testing.pytest_fixtures import spark_session
|
|
416
|
+
|
|
417
|
+
def test_my_function(spark_session):
|
|
418
|
+
# ... test code ...
|
|
419
|
+
# spark_session is automatically cleaned up
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Requirements
|
|
423
|
+
|
|
424
|
+
- Python 3.9+
|
|
425
|
+
- pytest 7.0+
|
|
426
|
+
|
|
427
|
+
Optional:
|
|
428
|
+
- sparkless>=3.19.0 (for mock engine)
|
|
429
|
+
- pyspark>=3.5.0 (for real engine)
|
|
430
|
+
- delta-spark>=3.0.0 (for Delta Lake support)
|
|
431
|
+
- pytest-xdist (for parallel test execution)
|
|
432
|
+
|
|
433
|
+
## License
|
|
434
|
+
|
|
435
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
436
|
+
|
|
437
|
+
## Links
|
|
438
|
+
|
|
439
|
+
- **GitHub**: [github.com/eddiethedean/sparkless-testing](https://github.com/eddiethedean/sparkless-testing)
|
|
440
|
+
- **Issues**: [github.com/eddiethedean/sparkless-testing/issues](https://github.com/eddiethedean/sparkless-testing/issues)
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
**Made with ❤️ for the data engineering community**
|
|
445
|
+
|