본문 바로가기

# Development/DevOps

[CI/CD] Github Actions로 Python CI 구축하기: Part2-Testing

Github Actions로 Python CI 구축하기: Part2-Testing

테스트는 소프트웨어 개발과 배포에서 핵심적인 부분으로, 코드의 품질을 보장하고 안정성을 높이는 과정이다.
 
이 글에서는 테스트 코드를 작성하는 방법과,

Github Actions을 통해 CI(지속적 통합)과정에서 테스트를 자동화 하는 방법을 정리했다.
 
CI/CD와 Github Actions에 대한 개념이 확실하지 않다면, 아래의 링크들을 읽어보길 바란다.

CI/CD

 

[CI/CD] CI/CD란 무엇인가?

CI/CD란 무엇인가? 소프트웨어 개발 및 배포를 자동화하여 효율적으로 개발 및 배포 프로세스를 관리하는 방법론이다. CI (Continuous Integration) Continuous Integration: 지속적 통합 CI는 개발자들이 코드를

yoon001.tistory.com

Github Actions

 

[CI/CD] GitHub Actions란?

Github Actions란? Github Actions는 소프트웨어 개발과 배포를 자동화하는 통합 서비스이다. 소프트웨어 개발 라이프사이클 안에서 Pull Request, Push 등의 이벤트 발생에 따라 자동화된 작업을 진행할 수

yoon001.tistory.com

 

테스트코드(Test Code) 란?

테스트 코드(Test code)는 소프트웨어의 기능과 동작을 테스트하는 데 사용되는 코드이다. 테스트 코들를 통해 개발자가 작성한 코드를 실행하고, 예상된 결과가 정상적으로 나오는지 확인한다.
 

테스트코드는 왜 작성할까?

코드 수정이 용이하다. 

테스트 코드를 작성하면 새로운 기능을 추가하거나 기존 코드를 수정할 때 이전 동작에 영향을 주어 예기치 않은 부작용을 일으키는지 확인할 수 있다. 같은 맥락으로 테스트 코드가 있는 경우 코드 리팩터링을 더 안전하게 수행할 수 있다. 
 

소프트웨어의 예상 동작을 문서화하는 역할을 한다.

테스트 코드가 해당 기능의 역할을 해야하는 역할을 명시하고 있기 때문에, 다른 개발자나 팀원이 코드의 의도와 사용 방법을 쉽게 이해하도록 도와준다.
 

반복적인 테스트 작업을 자동화 할 수 있다. 

자동화된 테스트는 생산성을 향상시키고 휴먼 에러를 줄인다. 
 

신뢰성 높은 배포를 할 수 있다. 

테스트 코드는 보통 CI/CD 파이프라인에서 사용되어 지속적으로 코드 변경을 검증하고 배포한다. 이로 인해 소프트웨어에서 발생할 수 있는 버그와 결함을 더 빨리 발견할 수 있다.
 
 

코드 파일 준비

CI/CD 테스트를 위해 만든 repository다. Frok로 복사해서 앞으로 진행될 과정들을 테스트 해볼 수 있다.

 

GitHub - sokuli-bit/study-ci-cd: CI/CD 테스트를 위한 레포지토리

CI/CD 테스트를 위한 레포지토리. Contribute to sokuli-bit/study-ci-cd development by creating an account on GitHub.

github.com

 

테스트코드 작성 방법

여기서는 파이썬 테스트에 pytest를 사용했다. pytest는 파이썬으로 작성된 테스트 프레임워크로, 파이썬 애플리케이션의 단위 테스트, 통합 테스트, 기능 테스트 등을 간편하게 작성하고 실행할 수 있는 도구이다.

 

pytest: helps you write better programs — pytest documentation

pytest: helps you write better programs The pytest framework makes it easy to write small, readable tests, and can scale to support complex functional testing for applications and libraries. pytest requires: Python 3.7+ or PyPy3. PyPI package name: pytest

docs.pytest.org

 

pytest 설치

pytest 사용을 위해 파이썬 환경에 pytest를 설치한다.

pip install pytest

테스트 작성

테스트는 test 혹은 tests의 폴더 내에 작성한다. 별도의 명령어 없이 pytest를 실행 할 경우, pytest가 tests 폴더를 찾아 내부의 테스트들을 모두 실행한다. 
 
가장 기본적인 형태의 tests의 예시는 아래와 같다.
테스트를 실행하는 파일의 이름은 반드시 'test_'로 시작해야 pytest가 테스트 파일로 인식 할 수 있다.

study-ci-cd
├── main.py
├── tests
│   ├── __init__.py
│   └── test_main.py
├── venv 
└── requirements.txt

 
그러면 이제 main.py에 대한 테스트 파일인 tests/test_main.py 파일을 작성해보자.

### main.py

import math
import os


def add(a, b) -> int:
    return math.floor(a + b)


def to_sentence(s) -> str:
    s = s.capitalize()

    if s.endswith("."):
        return s
    else:
        return s + "."
## test_main.py

import main


def test_add():
	# 정수 더하기 테스트
    assert main.add(3, 4) == 7
    
    # 소수 더하기 테스트
    assert main.add(3.5, 4) == 7
    assert main.add(3.9, 4) == 7
    assert main.add(3.9, 4.1) == 8


def test_to_sentence():
	# 마침표가 없을 때 단어 대문자 테스트
    assert main.to_sentence("apple") == "Apple."
    # 마침표가 없을 때 문장 대문자 테스트
    assert main.to_sentence("Apple trees") == "Apple trees."
    # 마침표가 있을 때 문장 대문자 테스트
    assert main.to_sentence("Apple trees.") == "Apple trees."

 

테스트 실행

위의 예시처럼 테스트를 작성하고 나면, 이제 테스트를 실행해 볼 차례다.
 
위의 예시 study-ci-cd 소스코드 파일 위치에서 pytest를 실행하면 된다.

~/study-ci-cd$ pytest

 
가상환경을 사용한다면, 가상환경 실행 후 pytest를 실행해야 한다.

(venv) ~/study-ci-cd$ pytest

 
특정 테스트 파일만 실행하는 것도 가능하다.

~/study-ci-cd$ pytest tests/test_main.py

 
테스트를 실행하고나면, 테스트 통과여부를 확인 할 수 있다.

 
테스트를 통과하지 않도록 코드를 수정해보자. 
main.py에서 add() 함수의 '+'를 '-'로 잘못 작성하고 pytest를 실행하면, 어디서 에러가 발생 했는지 확인 할 수 있다.

def add(a, b) -> int:
    return math.floor(a - b)

 

🔮 한걸음 더 

아래는 일반적인 python으로 만들어진 어플리케이션의 tests 폴더의 작성 예시다.

study-ci-cd
├── app.py
├── project
│   ├── __init__.py
│   ├── models.py
│   └── ...blueprint folders...
├── requirements.txt
├── tests
│   ├── conftest.py
│   ├── functional
│   │   ├── __init__.py
│   │   ├── test_books.py
│   │   └── test_users.py
│   └── unit
│       ├── __init__.py
│       └── test_models.py
└── venv

 
 

Github Actions로 CI(Testing) 구축하기

이제는 Github Actions를 사용해, CI 프로세스에 테스트 과정을 포함시켜보자.
Github Actions 기본적인 Workflow 설정 방법은 아래의 Part1-Linting 글을 읽어보길 바란다.

 

[CI/CD] Github Actions를 활용한 Python(Flask) CI 구축하기: Part1-Linting

Github Actions를 활용한 Python(Flask) CI 구축하기: Part1-Linting 본 글을 읽기전에 CI/CD와 Github Actions에 대한 개념이 확실하지 않다면, 아래의 링크들을 읽어보길 바란다. CI/CD [CI/CD] CI/CD란 무엇인가? CI/CD

yoon001.tistory.com

 
앞선 Linting 단계까지의 Workflow는 다음과 같다.

### python-lint.yml
# 워크플로우 이름
name: (PR) Format Python Code

# 워크플로우를 실행할 이벤트 설정
on:
  push:
    branches: [ "master" ]  # "master" 브랜치로 push가 발생할 때 이 액션을 실행
  pull_request:
    branches: [ "master" ]  # "master" 브랜치로 pull request가 발생할 때 이 액션을 실행

# 작업 정의
jobs:
  python-code-format:
    runs-on: ubuntu-20.04 # Ubuntu 20.04 환경에서 실행

    # 실행할 단계 정의
    steps:
      # GitHub 리포지토리를 체크아웃 (클론)
      - name: Checkout code
        uses: actions/checkout@v2

      # Python 환경 설정
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.10" # Python 3.10 버전 사용
          architecture: "x64" # 64비트 아키텍처 사용

      # Python 버전 확인
      - name: Display Python version
        run: python --version # Python 버전 표시

      # 필요한 Python 패키지 설치
      - name: Install packages
        run: pip install black isort autopep8

      # 코드 포맷팅 작업 수행
      - name: Formatter
        run: |
          black . # Black를 사용하여 코드 포맷팅
          autopep8 --recursive --in-place --aggressive --aggressive . # autopep8를 사용하여 코드 포맷팅
          isort . # isort를 사용하여 코드 포맷팅

      # Pull Request 생성
      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v3
        with:
          commit-message: Auto code format # 커밋 메시지
          title: Fixes by format action # 풀 리퀘스트 제목
          body: This is an auto-generated PR with fixes. # 풀 리퀘스트 본문
          labels: automated pr # 레이블
          branch: python-code-format-patches # 새로운 브랜치 이름

 

Workflow에 Testing  작업 추가

이제 위의 Workflow에 테스트를 위한 job을 추가해보자. 각 단계에 대한 설명은 주석으로 작성했다.

jobs:
  pytest:
  	# GitHub Actions를 실행할 환경 지정 (Ubuntu 20.04)
    runs-on: ubuntu-20.04  
    steps:
      # GitHub 리포지토리를 체크아웃하는 단계
      - uses: actions/checkout@v2
	
     # Python 환경을 설정하는 단계
      - uses: actions/setup-python@v4 
        with:
          python-version: "3.7"  # Python 버전을 지정 (여기서는 3.7 사용)
          architecture: "x64"  # 아키텍처 지정 (64비트 사용)

      - name: Display Python version  # 작업 이름 표시
        run: python --version  # 현재 Python 버전 출력
        
	# 필요한 Python 패키지 다운로드
      - name: Install packages
        run: |
        	pip install pytest
        	pip install -r requirements.txt
	
    # pytest 실행
      - name: Pytest 
        run: pytest

 
이제 기존의 Linting workflow와 합쳐진 형태는 다음과 같다.

name: CI Process

on:
  push:
    branches: [ master]
  pull_request:
    branches: [ master]
    
jobs:
  pytest:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v4
        with:
          python-version: "3.7"
          architecture: "x64"
      - name: Display Python version
        run: python --version
      - name: Install packages
        run: pip install -r requirements.txt
      - name: Pytest
        run: pytest

  code-format:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v4
        with:
          python-version: "3.7"
          architecture: "x64"
      - name: Display Python version
        run: python --version
      - name: Install packages
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install black isort autopep8
      - name: Formatter
        run: |
          black .
          autopep8 --recursive --in-place --aggressive .
          isort .          
      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v3
        with:
          commit-message: Auto Code Format
          title: Fixes by format action
          body: This is an auto-generated PR with fixes.
          labels: automated pr
          branch: python-code-format-patches

 

GIthub Actions 테스트

이제 CI 프로세스가 제대로 작동하는지 확인해볼 차례다.
변경 사항을 Push 하면, Actions 탭에서 다음과 같이 두개의 작업이 병렬적으로 실행되는 것을 확인 할 수 있다.

 
만약 pytest를 통과한 경우에만 code-format을 실행하고 싶다면,
code-format 내에 needs: pytest를 추가해주면 된다.

  code-format:
    runs-on: ubuntu-20.04
    needs: pytest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v4
        with:
          python-version: "3.7"
          architecture: "x64"

 
Workflow 후에 변경 사항을 Push 하면,
다음과 같이 pytest이후 cod-formant 작업이 순차적으로 실행되는 것을 확인 할 수 있다.

마무리

이 글에서는 Github Actions를 활용하여 Python 프로젝트에서 테스트 자동화를 설정하는 방법을 살펴보았다. 테스트 코드 작성의 중요성부터 Github Actions를 사용한 자동화 설정, 그리고 테스트 실행과 결과 확인까지 다루었다.

다음 글에서는 테스트가 얼마나 많은 코드를 커버하는지를 측정하는 방법인 코드 커버리지(Code Coverage)에 대해 정리해보려고 한다.