SeoulNative / naengboo

naengboo backend repo
2 stars 2 forks source link

Init pytest #13

Closed hoyeon94 closed 3 years ago

hoyeon94 commented 3 years ago

What

Why

Description

디렉터리 구조

image

tests/params/refrigerators_params.py

def test_post_ingredients():
    cast_list = [
        {
            "input": {
                "username": "db-post-case-1",
                "msg": "db-post-test-case-1",
            },
            "output": [
                {
                    'username': 'db-original',
                    'msg': 'original',
                },
                {
                    'username': 'db-post-case-1',
                    'msg': 'db-post-test-case-1',
                }
            ]
        },
        {
            "input": {
                "username": "db-post-case-2",
                "msg": "db-post-test-case-2",
            },
            "output": [
                {
                    'username': 'db-original',
                    'msg': 'original',
                },
                {
                    'username': 'db-post-case-2',
                    'msg': 'db-post-test-case-2',
                }
            ]
        },
    ]
    return [(test_case["input"], test_case["output"]) for test_case in cast_list]

params에는 위와 같이 json과 비슷한 형식으로 테스트 케이스를 저장합니다.

test/views/test_refrigerators.py

@pytest.mark.parametrize("input, output", params.test_post_ingredients())
def test_post_ingredients(client, input, output):
    response = client.post(
        "/refrigerators/ingredients",
        json=input,
    )

    response_json = loads(response.json)
    for i in range(len(output)):
        assert subset_checker(response_json[i], output[i])

views/refrigerators에 있는 Ingredients-post API를 테스트하는 함수입니다. input, output 인자를 함수로 넘겨주는 방식에 대해 궁금하시다면 pytest document - Parametrizing fixtures and test functions를 참고해주세요. assert 함수에서 subset_checker를 사용한 이유에 대해서는 아래 comment를 참고해주세요. 인자 중 client에 대해서는 아래 init.py를 설명하면서 다시 말씀드리겠습니다. 함수와 모듈 이름이 test로 시작하지 않으면 pytest가 인식을 못 합니다. 참고해주세요.

test/init.py

@pytest.fixture(scope='function')
def client():
    with flask_context.test_client() as client:
        load_documents()
        yield client
        flush_db()

def flush_db():
    for collection_name in db.list_collection_names():
        db.drop_collection(collection_name)

def load_documents():
    collection_json_file_path = path.join(path.dirname(__file__), "mock_data", '*')
    collection_json_file_ls = glob(collection_json_file_path)
    for collection_json_file_path in collection_json_file_ls:
        with open(collection_json_file_path, 'r') as file:
            data = file.read()
            data = loads(data)

        collection_name = Path(collection_json_file_path).stem

        if len(data) == 0:
            pass
        elif len(data) == 1:
            db[collection_name].insert_one(data[0])
        else:
            db[collection_name].insert_many(data)

pytest 구동 시 자동으로 실행되는 init 모듈입니다. 여기서 client라는 pytest fixture 함수를 선언하는데, scope를 function으로 해 놓으면 이후에 test 함수가 client인자를 받을 때 항상 저 함수를 실행하고 결과 값을 반환합니다. 즉, load_documents()와 flush_db()는 모든 테스트 케이스마다 실행됩니다.

테스트 실행 방법

rootdir에서 다음과 같은 명령어를 실행하면 pytest를 실행합니다.

python manage.py test
=================================================================================================================================================== test session starts ===================================================================================================================================================
platform darwin -- Python 3.9.4, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /Users/imhoyeon/Documents/seoulnative/naengboo_fork
collected 2 items                                                                                                                                                                                                                                                                                                         

test/views/test_refrigerators.py ..                                                                                                                                                                                                                                                                                 [100%]

====================================================================================================================================================

test/init.py, test/mock_data 관련 PR 중 변경 사항

fb55d86 4123656 8224464

디렉터리 구조 설계 시 참고한 자료

coloryourlife commented 3 years ago

이슈에서도 잠시 언급드렸지만 manage.py 에서는 모든 것을 불러와 실행하는 역할을 하고 cli 함수들은 따로 관리하는게 좋을 것 같다는 의견을 드립니다. 관련해서는 호연님 진행 중이신 장고 프로젝트가 있다면 site-packages에서 django / core / management 확인해보시면 바로 감 오실겁니다!

coloryourlife commented 3 years ago

귀찮으실 수는 있는데 기본적으로 PR의 설명을 조금 더 친절하게 세부적으로 해주시면 좋을 것 같습니다. Test를 위해 작성해야될 파일들이 기본적으로 어떤 것을 위한(?)것인지에 대한 대략적인 설명이 있으면 좋을 것 같습니다. 예를들어 제가 이해한대로라면 지금 테스트를 위해서는 params 파일 하나와 test파일을 하나 만들어서 params는 테스트 데이터?를 임의로 담는 것으로 보이는데 이런 것들을 간단하게만 PR의 Description에 간단하게 flow로 설명을 달아주시면 조금 더 수월하게 리뷰가 가능할 것 같습니다.

추가로 경민님 말씀에 동의를 하는 것이 현 pr에는 test와 cli에 대한 작업이 모두 이루어져있습니다. 두가지 작업을 하셔서 너무나 감사드리고 좋은데 PR은 두개로 쪼개는 것이 좋은 것 같습니다.

고생하셨습니다.

hoyeon94 commented 3 years ago

두 분 말씀대로 PR을 나누는 게 좋을 것 같습니다! 다만, PR을 나누려고 했으면 애초에 branch를 나눠서 작업했어야 하는데 제가 하나에다가 다 짬치기 하는 바람에 나누기가 조금 어려울 것 같습니다,, 이번 PR까지만 처리하고 다음부터 문제별로 브랜치 나눠서 작업해도 될까요?

coloryourlife commented 3 years ago

그러면 현재 PR 제목과 내용에라도 두가지 모두 반영을 해주세요.

livlikwav commented 3 years ago

두 분 말씀대로 PR을 나누는 게 좋을 것 같습니다! 다만, PR을 나누려고 했으면 애초에 branch를 나눠서 작업했어야 하는데 제가 하나에다가 다 짬치기 하는 바람에 나누기가 조금 어려울 것 같습니다,, 이번 PR까지만 처리하고 다음부터 문제별로 브랜치 나눠서 작업해도 될까요?

interactive rebase 같은걸로 수정할 수 있지 않나요? 혹은 branch 내의 특정 커밋을 복제해서 test 초기화 브랜치를 만들고, 기존 branch에는 test commit 내용을 삭제해서 cli 브랜치를 만들고 할 수 있을텐데요

git은 어짜피 앞으로 무조건 두고두고 쓰게될텐데, 이번 기회에 배워보시는 것이 어떨까요?

coloryourlife commented 3 years ago

두 분 말씀대로 PR을 나누는 게 좋을 것 같습니다! 다만, PR을 나누려고 했으면 애초에 branch를 나눠서 작업했어야 하는데 제가 하나에다가 다 짬치기 하는 바람에 나누기가 조금 어려울 것 같습니다,, 이번 PR까지만 처리하고 다음부터 문제별로 브랜치 나눠서 작업해도 될까요?

interactive rebase 같은걸로 수정할 수 있지 않나요? 혹은 branch 내의 특정 커밋을 복제해서 test 초기화 브랜치를 만들고, 기존 branch에는 test commit 내용을 삭제해서 cli 브랜치를 만들고 할 수 있을텐데요

몰라서 안하는거는 아닐것으로 생각됩니다

livlikwav commented 3 years ago

번거롭다면 이 PR을 수정하는 방식으로 해도 될 것 같네요

hoyeon94 commented 3 years ago

interactive rebase 같은걸로 수정할 수 있지 않나요? 혹은 branch 내의 특정 커밋을 복제해서 test 초기화 브랜치를 만들고, 기존 branch에는 test commit 내용을 삭제해서 cli 브랜치를 만들고 할 수 있을텐데요 git은 어짜피 앞으로 무조건 두고두고 쓰게될텐데, 이번 기회에 배워보시는 것이 어떨까요?

오늘 밤에 배우면서 천천히 해보겠습니당

hoyeon94 commented 3 years ago

귀찮으실 수는 있는데 기본적으로 PR의 설명을 조금 더 친절하게 세부적으로 해주시면 좋을 것 같습니다. Test를 위해 작성해야될 파일들이 기본적으로 어떤 것을 위한(?)것인지에 대한 대략적인 설명이 있으면 좋을 것 같습니다. 예를들어 제가 이해한대로라면 지금 테스트를 위해서는 params 파일 하나와 test파일을 하나 만들어서 params는 테스트 데이터?를 임의로 담는 것으로 보이는데 이런 것들을 간단하게만 PR의 Description에 간단하게 flow로 설명을 달아주시면 조금 더 수월하게 리뷰가 가능할 것 같습니다.

요 내용은 PR쪼개고 나서 다시 정리하겠습니다

do168 commented 3 years ago

내가 만든 기능을 테스트하는 것도 중요한만큼, 그 기능을 구현하기 위해 짠 코드의 커버리지를 보는 것도 디폴트로 설정하면 좋을 것 같은데 coverage run -m pytest .... 명령어로 테스트를 하는 것은 어떤가요?

hoyeon94 commented 3 years ago

내가 만든 기능을 테스트하는 것도 중요한만큼, 그 기능을 구현하기 위해 짠 코드의 커버리지를 보는 것도 디폴트로 설정하면 좋을 것 같은데 coverage run -m pytest .... 명령어로 테스트를 하는 것은 어떤가요?

좋은 생각인 것 같아요! 다만, 테스트 케이스와 테스트 함수를 모두 다 짜고 실질적인 개발이 이루어 지기 전까지는 coverage를 추가해도 사용할 수는 없을 것 같습니다! 저는 coverage를 남이 쓰라는 대로 그냥 써보기만해서 어떻게 하면 잘 적용할 수 있을지 잘 모르겠습니다,, 이후에 공부좀 더 해보고 우찬님 말씀대로 추가해보도록 할게요! test 관련 이슈에 기록해놓겠습니다.