테스트 종류 (유닛/통합/E2E, fake/mock)

jjin·2024년 3월 25일
0

$pip install pytest

prefix로 test_를 붙여줘야 pytest가 인식 가능

유닛 테스트 (함수, 메서드)

def create_token(user_id: str) -> str:
	return user_id + "_verified"

def test_create_token():
	actual = create_token("grab")
    expected = "grab_verified"
    assert actual == expected # AssertionError

통합 테스트 (기능)

def login(user_id: str, user_pw: str) -> str:
	user_repository = UserRepository()
    user = user_repository.find_by_id(user_id)
    if user.id == user_id and user.pw = user_pw:
    	return create_token(user_id)
    else:
    	raise Exception("로그인 인증에 실패했습니다")
        
def test_login_successful():
	#given
    user_id = "grab"
    user_pw = "1234"
    
    #when
    actual = login(user_id, user_pw)
    
    #then
    assert actual == "grab_verified"
    
def test_login_failed():
	#given
    user_id = "grab"
    user_pw = "1234"

    #when & then
	with pytest.raises(Exception):
    	login(user_id, user_pw)

일반적으로 통합테스트는 외부 의존성을 포함하지만, 원래는 운영 환경과 분리되어야 한다. 예를 들어 운영 DB에 연동하면 안된다.
테스트에서 재현할 수 없는 외부 의존성은 테스트 더블(대체 객체)을 사용

E2E 테스트 (환경 구축까지)

# HTTP 요청으로 로그인 요청 시 애플리케이션은 웹 인터페이스를 제공해야
from fastapi import FastAPI

app = FastAPI()

class LoginReq(BaseModel):
	id: str
    pw: str
    
@app.get("/login")
def login_endpoint(req: LoginRequest):
	token = login(user_id=req.id, user_pw=req.pw)
    return {
    	"token": token,
    }
import requests

def test_login_endpoint():
	# given
    api_host = "localhost:8000"
    payload = {
    	id: "grab",
        pw: "1234"
    }
    
    # when
    res = requests.post(url=f"{api_host}/login", json=payload)
    
    # then
    assert res.data() == {
    	"token": "grab_verified"
    }

의존성을 대체하는 테스트 더블

dummy

class DummyRepository(Repository):
	def insert(self, data):
    	return True
        
    def find_by_id(self, user_id):
    	return "grab"

stub

dummy에서 최소한의 구현만 해둔. 미리 준비해둔 결과만을 반환

class StubUserRepository(Repository):
	def insert(self, data):
    	return "OK"
        
    def findById(self, user_id):
    	return {"id": user_id, "name": "test_grab", ...}

spy

stub에서 테스트에 필요한 정보를 기록해둠.

class SpyUserRepository(Repository):
	insert_called = 0
    
    def insert(self, data):
    	SpyUserRepository.insert_called += 1
        return "OK"
        
    @property
    def get_insert_called(self):
    	return SpyUserRepository.insert_called

fake (많이 사용)

class FakeUserRepository(Repository):
	def __init__(self):
    	self.users = []
        
    def insert(self, data):
    	self.users.append(data)
        
    def find_by_id(self, user_id):
    	return [user for user in self.users if user.id == user.id]
    	

mock (많이 사용)

행위 검증 함.(fake는 단순 값 반환) 여기선 메서드가 호출되었는지도 확인 가능

@mock.patch.object(UserRepository, 'insert')
def test(insert_method):
	insert_method.return_value = "OK"
    insert_method({"id": 1, "name": "grab"})
    
# requests라는 서드 파티 라이브러리에 mocking하는 사례
@patch("requests.get")
def test_get_user(mock_get):
	res = mock_get.return_value
    res.status_code = 200
    res.json.return_value = {
    	"name": "Test User",
        "email": "user@test.com"
    }
    user = get_user(1)
profile
진짜

0개의 댓글

관련 채용 정보