본문 바로가기
개발

[pytest] 파이썬 api 테스트

by 박영귤 2023. 8. 30.

 

1. UI test / End-to-End test :
UI에 직접 입력하고 기능이 정상 작동하는지, 화면에 정상으로 출력되는지 테스트해 보는 방식. 실제 서비스 사용과 같이 테스트할 수 있다. 하지만 시간이 많이 소요되고 모든 시스템을 실행 시키고 연결해야 테스트가 가능하다. Selenium 같은 UI 테스트 프레임워크로 어느 정도 자동화 가능하지만 완벽한 자동화엔 어려움이 있다.

2. Integration test :
테스트하고자 하는 시스템을 실행시켜 실행하는 테스트. 하나의 시스템만 테스트하므로 UI 테스트에 비해 실행 시간과 설정이 간단하고, UI 요소가 없는 시스템의 경우 테스트하기 용이하다.

3. Unit test :
시스템을 실행하여 테스트하기보다 코드를 직접 테스트하는 개념. 함수등 시스템을 이루는 구성 단위를 독립적으로 테스트한다. 실행하기 쉽고 빠르며, 디버깅이 비교적 쉽다. 하지만 전체적인 부분을 테스트하기에는 제한적이다.

⚠️ 아래 단계일 수록 테스팅과 디버깅이 쉽기 때문에 전체 테스트에서 더 많은 비중을 둬야한다 ⚠️
⚠️ 테스트를 다음 단계로 미루는 것은 버그 발견을 미루고 수정을 힘들게 할뿐이다 ⚠️

출처 : https://velog.io/@sangmin7648/Flask-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%98%EA%B8%B0-pytest


간단한 사용방법

지금까지 나는 UI테스트만 진행했었다. 이렇게 하니까 시간이 굉장히 많이 소요되고, 자동화가 되어있지 않으니 실수를 할 수 밖에 없다는 것을 알았다. 또한, api가 많아질 수록, 예전 것은 테스트를 진행하는데 어려움이 있어 더 나은 방법을 찾고 있다.

 

검색하다가 pytest라는 것을 알게 되었다. 사용법은 간단하다. 파이썬 파일을 하나 만들고, pytest -v a.py라는 명령어를 입력하면 그 파일에 있는 "test_"로 시작하는 함수를 모두 실행시켜 failed인지 passed인지 출력해준다.

def setup_function():
    pass

def test_false():
    assert False
def test_true():
    assert True

관례상 파일 이름을 test_api.py라고 정하고 그 안에 위 코드를 입력하였다. 이제 터미널에 pytest -v test_api.py를 입력하니 아래와 같은 출력이 나왔다.

참고로 setup_function은 초기화작업을 실행시켜주는 부분이라고 생각하면 된다. 아래를 보면 알 수 있다.

a = []

def setup_function():
    a.append(1) 
    pass

def test_false():
    assert False

def test_true():
    assert True

def test_setup0():
    assert len(a)==0

def test_setup1():
    assert len(a)==1

각 테스트 함수를 실행시킬 때 마다 setup_function을 실행시켜주는 것으로 보인다. 따라서 test_setup0함수에서는 원소가 3개이고, test_setup1함수에서는 원소가 4개이다.

테스트 skip 하는 법

1. pytest.skip

def test_false():
    pytest.skip("메롱")
    assert False

위처럼 pytest.skip을 함수를 호출하면, 그 아래 코드는 모두 무시된다. 

이런 출력이 뜨게 된다.

2. pytest.mark.skip, pytest.mark.skipif

import pytest

@pytest.mark.skip("skip~")
def test_1():
    assert False

@pytest.mark.skipif(1 == 1, reason="skip~")
def test_2():
    assert True

@pytest.mark.skipif(1 == 0, reason="skip~")
def test_3():
    assert True

pytest.mark.skip 데코레이터를 함수 위에 작성해주면 아예 테스트 함수를 무시한다.

skipif는 이름 그대로 조건이 맞으면 무시하게 된다.

또한, api가 많아지면 테스트 시간이 오래걸릴 수도 있다. 이를 대비해 테스트를 병렬로 실행할 수 있도록 지원해준다. 
pytest-xdist 플러그인을 사용하면 되는데, 이는 추후에 필요하면 사용할 예정이다. 사용법은 간단해서 검색해보자!
Fixture을 사용한 파라미터 넘기기
@pytest.fixture(scope="module", params=[1, 2, 3])
def parametrized_fixture(request):
    param = request.param
    param += 1
    # param을 사용하여 Fixture 설정
    return param

def test_using_fixture(parametrized_fixture):
    assert parametrized_fixture == 0

이렇게 하면 각각의 파라미터를 받아서 함수에 넣은 후, 그 리턴값을 테스트함수에서 사용할 수 있게 된다.

이 사진을 보면 parametrized_fixture의 값이 2, 3, 4가 된 것을 볼 수 있다. 즉 parametrized_fixture함수의 리턴값을 그대로 받은 것이다. 코드의 가독성을 높여줄 수 있을 것 같다.


이제 테스트용 db 구축 및 다른 파일에서 api호출만 하면 api테스트 자동화를 할 수 있다.

테스트용 db 구축
import pytest
import sqlite3
import os

@pytest.fixture(scope="module")
def db_connection():
    # 데이터베이스 연결 생성
    os.system("cp source.db test.db")
    connection = sqlite3.connect("test.db")
    yield connection
    # 테스트 후에 데이터베이스 연결 닫기
    connection.close()
    os.remove("test.db")

@pytest.fixture(scope="function")
def db_cursor(db_connection):
    # 커서 생성
    cursor = db_connection.cursor()
    yield cursor
    # 테스트 후에 커서 닫기
    cursor.close()

def test_insert_data(db_cursor):
    db_cursor.execute("INSERT INTO ip (title, artist_id, type, width, depth, height, meterial, photo_id, created_at) VALUES ('1', '472dad16-8e19-44eb-bab6-6ef9107d12de', 'object', 0, 0, 0, '12', '1', '2023-08-01 16:17:50.809597')")
    users = db_cursor.fetchall()
    assert True

def test_select_data(db_cursor):
    db_cursor.execute("SELECT * FROM ip")
    users = db_cursor.fetchall()
    assert len(users) == 14

sqlite는 파일시스템 기반의 데이터베이스여서 파일 복사 후 사용이 가능하다. 따라서 데이터베이스 복사 후 연결, 커서 생성 후 그것을 이용하여 테스트함수에서 데이터베이스를 사용 가능하다.

api 호출
def test_api(db_connection):
    # API 엔드포인트 URL
    url = "http://localhost:5000/api/api1"  # 예시 URL

    # API 요청 보내기
    response = requests.get(url)

    # 응답 확인
    assert response.status_code == 200  # 예상되는 HTTP 상태 코드

api의 url을 주면 간단하게 api를 테스트 할 수 있다. 

하지만, 저렇게 url을 주는 것이 아니라, 같은 폴더 내 다른 파일에 있는 코드를 불러와서 테스트하는 방법을 알 수 있으면 더 좋을 것 같다.

로그인 상태를 유지하는 토큰을 헤더에 넣어주기 위해선 다음과 같이 실행시키면 된다.

def test_api2(db_connection):
    # API 엔드포인트 URL
    url = "http://localhost:5000/api/api1"  # 예시 URL
    
    # login require를 위해서 헤더에 토큰을 넣어줌.
    headers = {
        "Authorization": "jwt token",  # 토큰 입력
    }
    # API 요청 보내기
    response = requests.get(url, headers=headers)

    # 응답 확인
    assert response.status_code == 200  # 예상되는 HTTP 상태 코드

 

'개발' 카테고리의 다른 글

파이썬 패키지  (1) 2023.11.22
[챗봇] 카카오톡 챗봇 만들기  (0) 2023.09.21
[python] 정규표현식  (0) 2023.08.28
relationship  (1) 2023.08.28
[Flask] traceback  (0) 2023.08.21