지난 프로젝트 소개 글에서 NEXT STEP 중 하나였던 테스트 도구 추가를 진행 중이다. 테스트 도입 계획부터 어떤 문제를 겪었고 어떻게 해결했는지를 이 글에서 다뤄보려고 한다.
테스트 도입 계획
테스트를 도입하기에 앞서, 다음과 같은 항목들을 우선해서 선정했다.
- 어떤 것을 테스트할 것인지?
- 어떤 도구(라이브러리)를 사용할 것인지?
- 테스트 코드는 어디에, 어떤 방식으로 구성할 것인지?
- 테스트는 어떻게 실행할 것인지?
1. 어떤 것을 테스트해야 할까?
Catch Me My Capital(이하 CMMC) 프로젝트는 기본적으로 금융 데이터를 수집한 뒤 적절히 변환해 데이터를 저장하고, 이를 대시보드로 시각화하는 "데이터 파이프라인"이 중심이다. 따라서, 데이터 파이프라인이 정상적으로 잘 작동하는지 판단할 수 있는 요소들이 테스트 대상이라고 생각했다.
- API 연결 및 응답 (Ingestion)
- 연결 상태 확인
- 정상 연결시 데이터 응답값 검증
- DAG 내 사용되는 함수 (Bronze & Silver Layer)
- 데이터 수집 및 처리함수
- 데이터 저장함수
- Glue Crawler 실행함수
- Glue job 실행함수
- Custom Operator에서 실행되는 함수
- Glue ETL job에 사용되는 spark script (Silver Layer)
- 데이터 변환 함수
- 데이터 저장 함수(to Redshift)
- dbt (SQL 쿼리)
먼저, 데이터 수집의 원천인 API가 정상적으로 잘 작동하는지 확인할 필요가 있다고 판단했다. API가 수정되었거나, 정상적인 응답 상태가 아니라면 뒤에서 실행될 코드의 의미가 사라지기 때문이다. API에 요청을 보내고 해당 응답의 상태 코드와 데이터를 검증하는 테스트가 필요하다고 생각했다.
두 번째로 Airflow 내 DAG에서 실행되는 함수 로직이 의도한대로 동작하는지에 대한 테스가 필요하다고 판단했다. 현재는 데이터 파이프라인이 정상적으로 작동되는 것을 확인했기 때문에 해당 로직에는 문제가 없겠지만, 테스트의 목적은 '코드의 변경 가능성'까지 염두에 두고 진행하는 것이기 때문이다. 추후 해당 함수들의 코드가 변경되더라도, 의도한대로 작동되고 있는지에 검증이 필요하다고 생각했다.
세 번째로 Glue ETL job에 사용되는 스크립트에 대한 검증이 필요하다고 판단했다. 위와 마찬가지로, 스크립트 코드가 변경됐을 때에도 우리가 의도한대로 데이터를 변환하고 저장하고 있는지에 대한 검증이 필요하다. 특히 Bronze Layer와 달리 Redshift에 테이블 형태로 저장되기 때문에, 쿼리에 대한 검증도 필요하다.
마지막으로 dbt에 사용되는 SQL 쿼리에 대한 검증도 필요하다고 판단했다. Gold Layer로 저장되는 데이터들은 Redshift에서 전달받은 뒤 dbt를 통해 변환되기 때문에, 쿼리의 유효성과 함께 코드가 변경되더라도 일관적인 처리를 하고 있는지에 대한 테스트가 필요하다고 생각했다.
위 항목 중 Airflow DAG 실행 자체에 대한 테스트는 빠졌다. 물론 테스트가 가능하긴 하다. 그러나 Airflow는 Web UI를 통해 실행 결과를 쉽게 알 수 있고, 대부분의 코드가 AWS와 연결을 요구하기 때문에 테스트 환경에서는 매우 많은 mock(모킹)처리가 필요하다는 점 등의 리소스 문제가 있어 항목에서는 빠지게 되었다. 대신 DAG에 사용되는 모든 함수들을 검증하는 테스트가 추가되었다.
2. 어떤 도구(라이브러리)를 사용할 것인가?
테스트 대상을 지정했다면, 이제는 어떤 도구를 사용해 어떻게 테스트할지 정해야 할 차례다. 위에서 정한 4가지 테스트 대상에 대해 아래와 같은 도구를 사용하기로 했다.
테스트 항목 | 테스트 도구 | 설명 |
API 연결 및 응답 | Postman, Github Actions | postman script를 github actions를 통해 실행 및 자동화 |
DAG 내 사용되는 함수 | pytest, moto | moto로 AWS 서비스 모킹 및 pytest로 실행 |
Glue ETL job에 사용되는 spark script | pytest, pyspark, moto | spark 코드는 pyspark로, moto로 AWS 서비스 모킹 및 pytest로 실행 |
dbt (SQL 쿼리) | dbt test | dbt test 기능으로 테스트 |
Postman
우선 API 연결 및 응답 테스트에는 Postman을 사용했다. 데이터 소스를 찾는 과정에서 Postman에 Collection 형태로 저장해 둔 덕에 쉽고 빠르게 테스트 환경을 만들 수 있었고, Postman API를 통해 Github Actions에서 자동화도 가능했기 때문이다.
pytest
DAG 내 사용되는 함수들과 Glue ETL job의 spark script는 기본적으로 pytest 라이브러리를 사용해 테스트를 진행했다. pytest는 간결하면서도 pytest-cov등의 풍부한 플러그인을 가지고 있어 실제 현업에서도 많이 쓰이는 라이브러리이며, 단위/API/E2E 등 다양한 테스트 유형을 가지고 있어 선택하게 되었다. 특히, 이미 만들어진 기능에 테스트를 더하는 것이기 때문에 pytest의 fixture 기능을 통해 테스트 환경을 용이하게 설정할 수 있다는 점을 높게 평가했다.
moto
DAG 또는 Glue에서 실행되는 함수들은 대부분 AWS 서비스와의 연결을 필요로한다. 하지만 테스트는 '함수' 자체가 잘 작동하는 것에 중점을 두고 있고, 인터넷과 보안 키 등이 없는 상황에서도 실행되어야 하기 때문에 해당 함수들을 그대로 사용할 수 없다. 또한 테스트 과정에서 실제 데이터 파이프라인에 불필요한 데이터가 저장될 수도 있다. moto 라이브러리를 이용하면, AWS 서비스를 모킹(mock: 모조품/모의객체)해 사용할 수 있어 실제 AWS 서비스와의 연결 없이 테스트 환경을 구축할 수 있다. (boto3를 mock 처리하면 --> moto)
dbt test
dbt 라이브러리는 dbt test라는 명령어를 통해 해당 쿼리가 정상적으로 작성되었는지에 대한 검증을 할 수 있다. 해당 명령어와 github actions를 통해 테스트를 자동화하려고 한다.
3. 테스트 코드는 어디에, 어떤 방식으로 구성할 것인가?
테스트 코드가 실제 데이터 파이프라인이 작동되는 코드와 함께 있을 경우 헷갈릴 뿐더러 충돌 위험성이 있다고 생각했다. 따라서, 테스트 코드 디렉토리를 따로 분리해 아래와 같은 방식으로 구성했다.
├── pytest.ini # 테스트 설정 파일
└── tests
├── conftest.py # pytest 공통 설정 및 기능 - fixture 등 (하위 디렉토리에서 모두 사용가능)
└── unit
├── extractors # 수집 및 저장 함수
│ ├── test_coin_extractor.py
│ ├── test_exchange_rate_extractor.py
│ ├── ...
├── oprators # custom operator ㅎ마수
│ ├── test_yf_operator.py
│ ├── ...
├── glue_scripts # glue script 함수
│ ├── test_coin_script.py
│ ├── ...
├── dbt
│ ├── test_dbt.py
│ └── ...
└── utils # 공통 사용 함수(s3 파일 저장 함수)
└── test_s3_utils.py
- pytest.ini: Pytest의 설정 파일로, 테스트 실행 시 필요한 여러 설정을 미리 정의한다. 테스트 파일 패턴을 지정하고, 기본 실행 옵션 및 환경 변수와 플러그인 등을 설정할 수 있다.
- tests/conftest.py: Pytest에서 공통적으로 사용되는 설정과 기능을 정의한다. Pytest가 자동으로 감지하므로 별도의 import없이도 여기에 정의된 기능(함수)을 하위 디렉토리의 테스트 코드에서 사용할 수 있다. 주로 fixture를 관리하고, 테스트 실행 전후의 작업을 세팅할 수 있다.(Hook 함수)
4. 테스트는 어떻게 실행할 것인가?
Postman, pytest, dbt등의 테스트 코드는 Github Actions 기능을 통해 CI 파이프라인을 설정한다. 해당 테스트들이 작동하는 workflow 파일을 생성해둔다면, Github에 새로운 코드가 push되거나 PR이 생성될 때 자동으로 테스트를 실행하고 결과를 확인할 수 있다.
여기까지 테스트 도입 환경 구축에 대해 작성했다. 다음 포스팅에서는 해당 테스트 코드들이 어떻게 작성되었고, 어떤 테스트 케이스(TC)를 포함하고 있는지에 대한 글을 작성해보려고 한다.
'Minding's Programming > Knowledge' 카테고리의 다른 글
[Python 3.13] Python에서 GIL을 비활성화 할 수 있다? (0) | 2025.02.24 |
---|---|
[QA/Testing] 모바일 앱 테스트 자동화 오픈소스 Appium 사용해보기 (0) | 2025.02.20 |
[QA/Testing] Charles를 이용해 테스트해보기 (Throttle test, Breakpoint test) (0) | 2025.02.15 |
[Postman/Github Actions] Github Actions를 통해 API 테스트 자동화하기 (0) | 2025.02.13 |
[QA/Testing] Charles Proxy Tool - 테스트 효율을 높일 수 있는 툴 (0) | 2025.02.07 |