ABAP

DB 없이 CDS 뷰 테스트 3가지 — CDS Test Double #shorts #SAP #ABAP

▶ YouTube에서 보기

개요 및 핵심 포인트

ABAP 개발에서 CDS(Core Data Services) 뷰는 데이터 모델링의 중심축이 되었습니다. 그러나 CDS 뷰 위에 비즈니스 로직을 쌓아 올릴수록, 이를 검증하기 위한 유닛 테스트는 점점 어려워집니다. 실제 데이터베이스에 의존하는 테스트는 느리고, 테스트 데이터를 통제하기 어렵고, CI 파이프라인에서 재현성이 떨어집니다. 이 글에서는 CL_OSQL_TEST_ENVIRONMENT를 활용한 CDS Test Double 기법을 단계별로 살펴봅니다.

  • 실제 DB 없이 CDS 뷰 동작을 검증하는 방법 이해
  • CL_OSQL_TEST_ENVIRONMENT로 의존성 격리 환경 구성
  • insert_test_data로 가상 데이터 주입 패턴 학습
  • CL_ABAP_UNIT_ASSERT 기반 결과 검증
  • 중첩 뷰, 어소시에이션, 파라미터 뷰의 모킹 전략
  • 프로덕션급 테스트 코드의 성능/보안/유지보수 패턴

먼저 알아두면 좋은 것들

이 글의 내용을 따라가려면 ABAP Unit 프레임워크의 기본 구조(DEFINITION FOR TESTING, FOR TESTING 메서드, setup/teardown)에 친숙해야 합니다. 또한 CDS View 정의 문법, 어소시에이션, 그리고 Open SQL(ABAP SQL)의 인라인 선언(INTO TABLE @DATA(...)) 사용 경험이 있으면 좋습니다. ADT(ABAP Development Tools, Eclipse) 환경에서 테스트 클래스를 실행해본 경험을 전제합니다.

환경 및 준비물

CDS Test Double 프레임워크는 SAP NetWeaver 7.51부터 도입되었고, S/4HANA 1809 이후 및 ABAP Platform 1909+에서 안정적으로 동작합니다. ABAP Cloud(SAP BTP, ABAP Environment)에서도 동일한 클래스로 사용 가능합니다.

  • 플랫폼: S/4HANA 1909 이상 또는 SAP BTP ABAP Environment
  • 커널: NW AS ABAP 7.52 이상 권장
  • IDE: ADT(Eclipse) — SAP GUI의 SE80에서는 CDS 작업이 제한적
  • 핵심 클래스: CL_OSQL_TEST_ENVIRONMENT, IF_OSQL_TEST_ENVIRONMENT, CL_CDS_TEST_ENVIRONMENT(7.55+)
  • 권한: 개발 패키지에 대한 테스트 클래스 생성 권한
참고로 7.55 이상에서는 CL_CDS_TEST_ENVIRONMENT가 추가로 제공되어, CDS 뷰의 의존성 체인(어소시에이션, 중첩 뷰)을 자동 분석해 모킹 범위를 좁혀줍니다. 하위 버전에서는 CL_OSQL_TEST_ENVIRONMENT를 사용합니다.

핵심 개념: CDS Test Double은 무엇을 모킹하는가

전통적인 ABAP 유닛 테스트의 가장 큰 골칫거리는 "데이터베이스 의존성"입니다. CDS 뷰는 결국 HANA 위에서 동작하는 SQL 객체이기 때문에, 일반적인 Mock 객체(CL_ABAP_TESTDOUBLE)로는 SELECT 결과를 가짜로 만들 수 없습니다. 여기서 등장하는 것이 OSQL Test Double입니다.

OSQL Test Double의 동작 원리를 비유하자면 "SQL 무대장치 교체"와 같습니다. 테스트 환경이 생성되면 프레임워크는 다음을 수행합니다.

  1. 대상 CDS 뷰/테이블과 동일한 구조의 임시 doubles를 메모리/임시 영역에 생성
  2. 해당 테스트 세션 동안 모든 ABAP SQL 호출을 가로채(intercept) 원본 대신 doubles로 라우팅
  3. 테스트 종료 시 doubles를 폐기하고 원본 라우팅을 복구

이 덕분에 테스트 코드에서는 평소처럼 SELECT ... FROM zi_sales_order를 작성해도, 실제 HANA 테이블이 아닌 우리가 insert_test_data로 주입한 데이터만 조회됩니다. CDS 뷰 내부의 조인, WHERE 절, 케이스 표현식, 어소시에이션 경로 표현식은 모두 그대로 실행되므로 "뷰 자체의 로직"을 검증하는 데 적합합니다.

다만 주의할 점은 "모든 SQL 기능이 동일하게 동작하지는 않는다"는 것입니다. HANA 고유의 일부 함수나 SQLScript 기반 AMDP는 doubles 환경에서 제한될 수 있습니다. 또한 doubles는 일반적으로 컬럼스토어의 통계나 인덱스를 가지지 않기 때문에 성능 테스트 목적으로는 부적합합니다. CDS Test Double의 목적은 어디까지나 기능 검증이며, 성능/체적 검증은 별도의 통합 테스트로 분리하는 것이 권장됩니다.

실전 코드: 3단계로 만드는 CDS 테스트

1단계: 가장 단순한 테스트 환경 만들기

첫 번째 예제는 단일 CDS 뷰 ZI_SALES_ORDER를 대상으로 테스트 환경을 생성하는 코드입니다. i_dependency_list 파라미터에 모킹할 객체 이름을 넘기면, 프레임워크가 의존하는 베이스 테이블까지 자동으로 doubles로 만들어줍니다.

CLASS ltc_cds_test DEFINITION FOR TESTING
  RISK LEVEL HARMLESS DURATION SHORT.

  PRIVATE SECTION.
    DATA: mo_env TYPE REF TO if_osql_test_environment.

    METHODS setup.
    METHODS teardown.
    METHODS test_cds_view FOR TESTING.
ENDCLASS.

CLASS ltc_cds_test IMPLEMENTATION.
  METHOD setup.
    mo_env = cl_osql_test_environment=>create(
      i_dependency_list = VALUE #(
        ( 'ZI_SALES_ORDER' )
      )
    ).
  ENDMETHOD.

  METHOD teardown.
    mo_env->clear_doubles( ).
  ENDMETHOD.

  METHOD test_cds_view.
    " 이 시점에서는 doubles가 비어 있음
    SELECT COUNT(*) FROM zi_sales_order INTO @DATA(lv_cnt).
    cl_abap_unit_assert=>assert_equals( act = lv_cnt exp = 0 ).
  ENDMETHOD.
ENDCLASS.

RISK LEVEL HARMLESSDURATION SHORT는 거의 필수입니다. doubles는 운영 데이터에 영향을 주지 않으므로 안전하며, 메모리 기반이라 실행도 매우 빠릅니다. teardown에서 clear_doubles를 호출하면 다음 테스트가 깨끗한 상태에서 시작됩니다.

2단계: 테스트 데이터 주입과 에러 처리

실무에서는 단순히 빈 환경이 아니라, 특정 시나리오를 재현할 데이터를 주입해야 합니다. insert_test_data는 내부 테이블을 받아 해당 뷰/테이블 형태의 doubles에 채워 넣습니다. 동시에 예외 처리와 로깅도 함께 고려해야 합니다.

CLASS ltc_cds_test IMPLEMENTATION.
  METHOD setup.
    TRY.
        mo_env = cl_osql_test_environment=>create(
          i_dependency_list = VALUE #( ( 'ZI_SALES_ORDER' ) )
        ).
      CATCH cx_osql_test_env INTO DATA(lx_env).
        cl_abap_unit_assert=>fail(
          msg    = |테스트 환경 생성 실패: { lx_env->get_text( ) }|
          detail = lx_env->get_longtext( )
        ).
    ENDTRY.

    DATA(lt_mock_data) = VALUE zi_sales_order_tab(
      ( sales_order = '0000000001'
        customer    = 'CUST001'
        net_amount  = '1000.00'
        currency    = 'KRW'
        status      = 'A' )
      ( sales_order = '0000000002'
        customer    = 'CUST002'
        net_amount  = '2500.00'
        currency    = 'KRW'
        status      = 'B' )
      ( sales_order = '0000000003'
        customer    = 'CUST001'
        net_amount  = '3000.00'
        currency    = 'KRW'
        status      = 'A' )
    ).

    mo_env->insert_test_data( lt_mock_data ).
  ENDMETHOD.

  METHOD test_filter_by_status.
    SELECT sales_order, customer, net_amount
      FROM zi_sales_order
      WHERE status = 'A'
      INTO TABLE @DATA(lt_result).

    cl_abap_unit_assert=>assert_equals(
      act = lines( lt_result )
      exp = 2
      msg = 'status=A 레코드는 2건이어야 함'
    ).
  ENDMETHOD.
ENDCLASS.

주의할 점은 insert_test_data가 받는 내부 테이블의 타입이 대상 객체의 라인 타입과 호환되어야 한다는 것입니다. CDS 뷰의 경우 보통 SE11/ADT에서 자동 생성된 tabl 형식을 사용하거나, TYPES lt TYPE TABLE OF zi_sales_order로 선언합니다. 키 필드 누락 시 런타임에 데이터가 잘못 채워질 수 있으므로, 항상 키를 명확히 지정하세요.

3단계: 프로덕션급 — 여러 뷰, 어소시에이션, 어서션 전략

실제 프로젝트에서는 하나의 CDS 뷰가 여러 베이스 테이블과 다른 CDS 뷰를 참조합니다. 이때는 의존성 목록에 관련 객체를 모두 명시하거나, 7.55+의 CL_CDS_TEST_ENVIRONMENT를 사용해 자동 분석을 활용합니다.

CLASS ltc_sales_order_prod DEFINITION FOR TESTING
  RISK LEVEL HARMLESS DURATION SHORT.

  PRIVATE SECTION.
    CLASS-DATA: mo_env TYPE REF TO if_cds_test_environment.

    CLASS-METHODS: class_setup, class_teardown.
    METHODS:
      setup,
      teardown,
      test_amount_aggregation FOR TESTING RAISING cx_static_check,
      test_currency_conversion FOR TESTING RAISING cx_static_check.
ENDCLASS.

CLASS ltc_sales_order_prod IMPLEMENTATION.
  METHOD class_setup.
    " 의존성 자동 분석 (7.55+)
    mo_env = cl_cds_test_environment=>create(
      i_for_entity = 'ZC_SALES_ORDER_REPORT'
    ).
  ENDMETHOD.

  METHOD class_teardown.
    mo_env->destroy( ).
  ENDMETHOD.

  METHOD setup.
    mo_env->clear_doubles( ).
  ENDMETHOD.

  METHOD teardown.
    " 각 테스트 격리
    mo_env->clear_doubles( ).
  ENDMETHOD.

  METHOD test_amount_aggregation.
    DATA(lt_orders) = VALUE zi_sales_order_tab(
      ( sales_order = '0001' customer = 'C1' net_amount = '100.00' currency = 'KRW' status = 'A' )
      ( sales_order = '0002' customer = 'C1' net_amount = '200.00' currency = 'KRW' status = 'A' )
      ( sales_order = '0003' customer = 'C2' net_amount = '300.00' currency = 'KRW' status = 'B' )
    ).
    mo_env->insert_test_data( lt_orders ).

    SELECT customer, SUM( net_amount ) AS total
      FROM zc_sales_order_report
      WHERE status = 'A'
      GROUP BY customer
      INTO TABLE @DATA(lt_agg).

    cl_abap_unit_assert=>assert_equals( act = lines( lt_agg ) exp = 1 ).
    cl_abap_unit_assert=>assert_equals(
      act = lt_agg[ 1 ]-total
      exp = '300.00'
      msg = 'C1 고객의 status=A 주문 합계'
    ).
  ENDMETHOD.
ENDCLASS.

프로덕션 코드에서 챙겨야 할 포인트는 세 가지입니다.

  • 성능: class_setup에서 환경을 한 번만 생성하고, 각 테스트는 clear_doubles로 데이터만 리셋합니다. 환경 생성은 비용이 크므로 메서드마다 만들지 마세요.
  • 격리성: 테스트 간 데이터 오염을 막기 위해 setupteardown 양쪽에서 clear_doubles를 호출하면 안전합니다.
  • 보안: 테스트 클래스에 실 데이터를 하드코딩하지 마세요. 가공된 가상 키(CUST001 등)를 사용하는 것이 일반적으로 권장됩니다.

흔한 실수와 트러블슈팅

Q1. CX_OSQL_TEST_ENV_TABLE_CREATE 예외가 발생합니다.
대부분 의존성 목록에 빠진 객체가 있거나, CDS 뷰가 활성화되지 않은 상태입니다. ADT에서 대상 뷰의 활성화 상태를 확인하고, 뷰가 참조하는 모든 베이스 객체(어소시에이션 타깃 포함)를 i_dependency_list에 명시해야 합니다. 7.55+에서는 CL_CDS_TEST_ENVIRONMENT를 사용하면 이 작업이 자동화됩니다.

Q2. 테스트는 통과하는데 운영 데이터에서 결과가 다릅니다.
이는 CDS Test Double의 가장 흔한 함정입니다. doubles는 HANA의 일부 고급 함수(WINDOW 함수의 특정 옵션, AMDP 호출 등)를 완전히 동일하게 시뮬레이션하지 못할 수 있습니다. 핵심 비즈니스 룰은 doubles 기반 유닛테스트로, 복잡한 HANA-specific 로직은 별도의 통합 테스트(실제 DB 사용)로 이중 검증하는 전략이 일반적으로 안전합니다.

Q3. insert_test_data 후에도 SELECT 결과가 비어 있습니다.
첫째, 클라이언트(MANDT) 필드를 확인하세요. doubles는 현재 클라이언트로 자동 채워주지만, 명시적으로 다른 클라이언트를 넣으면 보이지 않습니다. 둘째, 테스트 메서드가 환경 생성 이전에 실행되었는지 확인하세요. class_setupsetup의 실행 순서를 정확히 이해해야 합니다. 셋째, 모킹 대상 뷰 이름의 대소문자를 확인하세요. 일반적으로 대문자로 통일하는 것이 권장됩니다.

Q4. 테스트가 너무 느립니다.
환경 생성을 setup(메서드별)에서 하지 말고 class_setup(클래스별)로 옮기세요. 또한 의존성 목록이 너무 광범위하면 doubles 생성 시간이 길어지므로, 실제로 SELECT에 등장하는 객체만 포함하도록 좁히는 것이 좋습니다.

다음 단계와 관련 주제

CDS Test Double을 익혔다면 다음 단계로 RAP(RESTful ABAP Programming Model) 환경의 비즈니스 객체 테스트 프레임워크인 BO Test Double(CL_BOTD_TXBUFDBL_BO_TEST_ENV)을 살펴보세요. RAP BO의 동작(Action, Determination, Validation)까지 모킹할 수 있습니다. 또한 ABAP Unit과 ATC(ABAP Test Cockpit)를 CI/CD 파이프라인(gCTS, abapGit, Piper)과 연동하면 모든 커밋마다 자동으로 doubles 기반 테스트를 실행할 수 있습니다. AMDP를 사용하는 경우에는 CL_AMDP_TEST_FRAMEWORK도 함께 학습하면 좋습니다.

참고 자료

댓글 0

아직 댓글이 없습니다.