Image_Us-테스트코드

codakcodak·2023년 2월 28일
0

ImageUs

목록 보기
11/17
post-thumbnail

문제상황

  • 배포나 개발 시 app을 구동할 때 예외케이스 때문에 서비스가 동작이 되자 않거나 에러가 뜨는 경우가 생김

해결방법

  • pytest를 이용한 테스크코드를 작성함으로서 개발 단계 및 배포 전 가능한 한 많은 케이스를 짜두고 안정적인 서비스를 개발

적용과정

  1. pytest 설치
pip install pytest
  1. 테스트 폴더구조

*models.py에는 test_model.py,test_service.py,test_view.py에 공통적으로 쓸 함수를 작성

*test_model.py에는 model계층을 테스트하는 코드를 작성

*test_service.py에는 service계층을 테스트하는 코드를 작성

*test_view.py에는 view계층을 테스트하는 코드를 작성

  1. 테스트 방식 설정
  • 정보를 불러오는 기능은 검증된 데이터를 가져오는 함수로 비교
    ex)
    def test_get_user_info(user_service):
        #1번 유저 정보 확인
        user_info=user_service.get_user_info(1)
        #방식1
        assert user_info=={
        	'email':'test@test.com',
            'name':'test',
            'id':1
        }
        #방식2
        assert user_info==get_user_info(1)
        #존재하지 않는 유저 정보 확인
        user_info=user_service.get_user_info(100)
        assert user_info==None
    *위는 db상의 정보를 불러오는 것이므로 하드하게 비교하는 방식 1보다 이미 검증된 데이터를 가져오는 get_user_info함수를 미리 선언하여 방식2처럼 작성
  • 정보를 수정 및 삭제하는 기능은 기존의 데이터를 미리 확인하고 변환할 데이터와 직접 비교

    ex)

    def test_update_user_info(user_service):
    	#업데이트 하려는 유저의 기존 정보 미리 확인
        user_info=get_user_info(1)
        assert user_info=={
            'id':1,
            'name':'test1',
            'email':'test1@naver.com',
            'profile':'testuser1'
        }
    	
        #업데이트 항목 설정
        update_user={
            'name':'updateuser1',
            'profile':'updatetestuser1'
        }
       #유저 업데이트 result=user_service.update_user_info(1,update_user)
        assert result==2
    
        user_info=get_user_info(1)
    		
        assert user_info=={
            'id':1,
            'name':update_user['name'],
            'email':'test1@naver.com',
            'profile':update_user['profile']
        }

    *위는 db상에 유저의 데이터가 수정이 되었기 때문에 변환시켜준 데이터로 직접 비교

  1. model.py 작성
import sys,os
sys.path.append((os.path.dirname(os.path.abspath(os.path.dirname(__file__)))))
import config
from sqlalchemy import create_engine,text

database=create_engine(config.test_config['DB_URL'],encoding='utf-8',max_overflow=0)

def get_user_id_and_password(email,type):
    result=database.execute(text("""
            select
                id,
                hashed_password
            from users
            where email=:email
            and type=:type
            """),{'email':email,'type':type})
    row=result.fetchone()
    result.close()

    user_id_and_password={
            'id':row['id'],
            'hashed_password':row['hashed_password']
        } if row else None
        
    return user_id_and_password
    
def get_room_userlist(room_id):
    rows=database.execute(text("""
            select
                r_u.user_id as id,
                u.name,
                u.email,
                u.profile
            from rooms_user_list as r_u
            left join users as u
            on r_u.user_id=u.id
            where r_u.room_id=:room_id
            and u.deleted=0
            and r_u.deleted=0         
            """),{
                'room_id':room_id
            }).fetchall()
...

*test_model.py,test_service.py,test_view.py의 세 테스트 파일에서 공통적으로 사용할 mysql_engine을 database로 선언

  1. 테스트 기본 설정
  • setup_function를 사용하여 시작 전 초기화 및 기본 데이터 삽입
def setup_function():
    print("============setup_func============")
    print("초기화")
    database.execute(text("""
        truncate users
    """))
    new_users=[{
        'id':1,
        'name':'test1',
        'email':'test1@naver.com',
        'profile':'testuser1',
        'hashed_password':hashed_password
    },{
        'id':2,
        'name':'test2',
        'email':'test2@naver.com',
        'profile':'testuser2',
        'hashed_password':hashed_password
    }]
    database.execute(text("""
        insert into users (
            id,
            name,
            email,
            profile,
            hashed_password
        ) values (
            :id,
            :name,
            :email,
            :profile,
            :hashed_password
        )
    """),new_users)
  • teardown_function을 이용하여 테스트 마무리 후 데이터 초기화
def teardown_function():
    print("============teardown_func============")
    print("초기화")
    database.execute(text("""
        truncate users
    """))
  • fixture 설정을 통해 매번 테스트 전 미리 클래스 선언 설정
@pytest.fixture
def user_service():
    return UserService(UserDao(database),config.test_config)

@pytest.fixture
def room_service():
    return RoomService(RoomDao(database),config.test_config)
    email_auth_info=user_service.get_email_auth_info(sample_email)
    assert email_auth_info=={
        'email':sample_email,
        'auth_password':"1234",
        'activated':0
    }

*위처럼 fixture를 선언해 놓고 선언한 함수 이름을 test_get_email_auth_info의 인자로 넣으면 test_get_email_auth_info가 실행전 미리 선언한 함수의 결과값을 인자로 받는다.

  1. 계층별 테스트코드 작성
    (test_model.py,test_service.py,test_view.py)
  • test_model.py
    (3계층의 model에 있는 모든 함수를 테스트)
...
#새로운 유저 가입 검증
def test_insert_user(user_dao):
    #type이 기본 회원가입인 유저
    new_user={
        'name':'test4',
        'email':'test4@naver.com',
        'profile':'testuser4',
        'password':'test_password',
        'type':'image_us'
    }
    hashed_password=bcrypt.hashpw(new_user['password'].encode('utf-8'),bcrypt.gensalt())
    new_user['hashed_password']=hashed_password
    
    new_user_id=user_dao.insert_user(new_user)
    user=get_user_info(new_user_id)
    
    assert user=={
        'id':new_user_id,
        'name':new_user['name'],
        'email':new_user['email'],
        'profile':new_user['profile']
    }
...
  • test_service.py
    (3계층의 service에 있는 모든 함수를 테스트)
...
#새로운 유저를 생성
def test_create_new_user(user_service):
    new_user={
        'name':'test4',
        'email':'test4@naver.com',
        'profile':'testuser4',
        'password':'test_password'
    }
    #새로운 유저를 만들고 정보를 확인
    new_user_id=user_service.create_new_user(new_user)
    new_user=get_user_info(new_user_id)
    
    assert new_user=={
        'id':new_user_id,
        'name':new_user['name'],
        'email':new_user['email'],
        'profile':new_user['profile']
    }
...
  • test_view.py
    (3계층의 view에 있는 모든 함수를 테스트)
...
@pytest.fixture(scope='session')
def api():
    app=create_app(test_config=config.test_config)
    app.config['TEST']=True
    api=app.test_client()

    return api
...

#유저의 정보 불러오기
def test_get_user(api): 
    #존재하는 유저 정보 확인
    user_id=1
    resp=api.get(f"/user/{user_id}")
    assert resp.status_code==200
    resp_json=json.loads(resp.data.decode('utf-8'))
    assert resp_json=={
        'user_info':
        {
            'id':1,
            'name':'test1',
            'email':'test1@naver.com',
            'profile':'testuser1'
        }
    }

    #존재하지 않는 유저 정보 확인
    user_id=100
    resp=api.get(f"/user/{user_id}")
    assert resp.status_code==404
...
  1. 테스트 결과
  • 명령어 입력
pytest test_model.py -vv
pytest test_service.py -vv
pytest test_view.py -vv

*pytest -vv만 입력하면 모든 파일의 모든 test키워드가 붙은 함수가 실행된다.

  • test_model.py

  • test_service.py

  • test_view.py

profile
숲을 보는 코더

0개의 댓글