[테스트 코드] 종류별 테스트 코드 작성

유닛 테스트

  • 로그인 기능에는 다음처럼 로그인 인증을 마친 유저에게 응답으로 줄 토큰을 생성하는 함수를 사용한다.
1
2
def create_token(user_id: str) -> str:
    return user_id + "_verified"
  • 물론 실제로 토큰 발급을 절대 위와 같이 하지 않는다.
  • 코드 예제를 심플하게 가져가기 위해서 간략히 표현했다.
  • 유닛 테스트는 함수나 메서드 같이 작은 단위의 코드에서 입출력의 결과가 올바른지를 확인하는 테스트이다.
  • 위 함수는 유닛 테스트를 하기 좋은 대상이다.
  • 다음처럼 유닛 테스트 코드를 작성해볼 수 있다.
1
2
3
4
def test_create_token():
    actual = create_token("6mini")
    expected = "6mini_verified"
    assert actual == expected

통합 테스트

  • 로그인 기능에는 다음처럼 로그인 로직을 수행하는 함수를 사용한다.
1
2
3
4
5
6
7
def login(user_id: str, user_password: str) -> str:
    user_repository = UserRepository()  # DB와 연동되어 User 정보를 저장하고 불러오는 객체
    user = user_repository.find_by_id(user_id)
    if user.id == user_id and user.password == user_password:
        return create_token(user_id)
    else:
        raise Exception("로그인 인증에 실패했습니다.")
  • 통합 테스트는 여러 요소를 통합한 프로세스를 검증하는 테스트이다.
  • login() 함수는 user_repositorycreate_token()에 의존하고 있는 통합적인 로직을 가진 함수이다.
  • 이 함수에 대해 다음처럼 통합 테스트 코드를 작성해볼 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def test_login_successful():
    # given
    user_id = "6mini"
    user_password = "1234"
    
    # when
    actual = login(user_id, user_password)
    
    # then
    assert actual == "6mini_verified"
    
    
def test_login_failed():
    # given
    user_id = "6mini"
    user_password = "wrong password"
    
    # when & then
    with pytest.raises(Exception):
        login(user_id, user_password)
  • 위 코드가 성공하려면 login() 함수 내부의 UserRepository 인스턴스와 create_token() 함수 역시 잘 작동해야 한다.
  • 이처럼 통합 테스트는 의존성 있는 객체들의 정상 작동 여부까지 포함하는 테스트이다.
  • 일반적으로 통합 테스트는 외부 의존성을 포함하고 있는 경우가 많다.
  • 하지만 테스트 환경은 운영 환경과 분리되어야 한다.
  • 예를 들어, 테스트 코드를 실행할 때 운영 DB에 연동하면 안된다.
  • 따라서 테스트에서 재현할 수 없는 외부 의존성(운영 데이터베이스, 운영 API 서버 등)은 테스트 더블을 사용한다.

E2E 테스트

  • 실제로 유저는 HTTP 요청으로 로그인을 요청하게 된다.
  • 따라서 애플리케이션은 다음과 같은 웹 인터페이스를 제공해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from fastapi import FastAPI

app = FastAPI()

class LoginRequest(BaseModel):
    id: str
    password: str

@app.get("/login")
def login_endpoint(req: LoginRequest):
    token = login(user_id=req.id, user_password=req.password)
    return {
        "token": token
    }
  • E2E 테스트는 최종 사용자 입장에서의 테스트이다.
  • 다음처럼 서버를 localhost:8000에 실행시킨 상황이라고 생각하고, 서버에 요청을 로그인 요청을 보내고 응답받는 테스트 코드를 작성한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests

def test_login_endpoint():
    # given
    api_host = "localhost:8000"
    payload = {
        "id": "6mini",
        "password": "1234"
    }
    
    # when
    res = requests.post(url=f"{api_host}/login", json=payload)
    
    # then
    assert res.data() == {
        "token": "6mini_verified"
    }
  • 위 테스트 코드가 성공하려면 localhost:8000에 실행한 서버가 잘 작동되어야 한다.
  • 이처럼 E2E 테스트는 서버 내부 동작은 전혀 관여하지 않은 채, 철저히 엔드 유저 입장에서 서버를 이용하는 시나리오대로 테스트해보는 것이다.
0%