Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Spark 변환의 행과 스키마 검사

이 테스트는 pytest-transforms의 실제 spark_session에서 CSV를 읽어 필터를 실행합니다. 출력에 쓸 DataFrame만 mock 핸들로 받아 확인하므로 실제 Foundry 데이터셋에는 기록하지 않습니다.

변환 모듈

src/myproject/datasets/find_aircraft_spark.py:

from pyspark.sql import functions as F
from transforms.api import Input, Output, transform


def compute_data(output, aircraft):
    rows = aircraft.dataframe()
    selected = rows.filter(
        (F.col("number_of_seats") > 300)
        & (F.col("operating_status") == "Yes")
    )
    output.write_dataframe(selected)


compute = transform.spark.using(
    output=Output("/Example Project/Outputs/aircraft_spark"),
    aircraft=Input("/Example Project/Inputs/aircraft"),
)(compute_data)

가상 자산 경로를 실제 값으로 바꾸고 해당 Spark API의 최소 버전 및 PySpark 테스트 환경을 준비합니다.

CSV 결과 비교

두 CSV를 src/test/resources/에 두고 src/test/test_find_aircraft_spark.py를 작성합니다.

from collections import Counter
from pathlib import Path
from unittest.mock import MagicMock

from myproject.datasets.find_aircraft_spark import compute_data

RESOURCES = Path(__file__).parent / "resources"


def test_find_aircraft(spark_session):
    source_frame = spark_session.read.csv(
        str(RESOURCES / "aircraft_mock.csv"), header=True, inferSchema=True
    )
    expected = spark_session.read.csv(
        str(RESOURCES / "expected_filtered_aircraft.csv"),
        header=True,
        inferSchema=True,
    )
    aircraft = MagicMock(spec_set=["dataframe"])
    aircraft.dataframe.return_value = source_frame
    output = MagicMock(spec_set=["write_dataframe"])

    compute_data(output=output, aircraft=aircraft)

    output.write_dataframe.assert_called_once()
    actual = output.write_dataframe.call_args.args[0]
    assert actual.columns == expected.columns
    assert actual.schema == expected.schema
    actual_rows = actual.collect()
    expected_rows = expected.collect()
    assert len(actual_rows) == len(expected_rows)
    assert Counter(tuple(row) for row in actual_rows) == Counter(
        tuple(row) for row in expected_rows
    )

예제 CSV는 작고 모든 열이 해시 가능한 스칼라이므로 결과를 한 번씩 collect하여 순서와 무관한 다중 집합으로 비교합니다. 중복 행의 개수도 유지합니다. 큰 데이터 전체를 테스트 목적으로 collect하는 패턴으로 확대하지 않습니다.

분산 DataFrame 상태로 비교해야 한다면 같은 열 순서와 호환 schema에서 양방향 exceptAll이 모두 비어 있는지 확인하는 방법도 사용할 수 있습니다. 한쪽 차집합만 확인하면 반대쪽의 추가 행을 놓칠 수 있습니다.

spark_session은 테스트 session에서 공유합니다. Time zone이나 Spark 설정을 바꾸는 테스트는 종료 전에 원래 값을 복원해야 하며, 다른 테스트의 실행 순서에 의존하지 않습니다.