티스토리 뷰

개발/python

다시쓰는 Flask unittest (상편)

Jaeyeon Baek 2019.06.07 22:26

http://flask.pocoo.org/docs/1.0/

우리가 개발한 프로그램을 테스트하는 방법론이 여러가지 나오고 있는데 이번에는 그 기본이 되는 unittest를 flask와 연계해서 알아보고자 합니다. 인터넷에 flask와 unittest로 검색해보면 Flask 공식 홈페이지를 포함해서 여러자료가 리스트 되지만 대부분 공식 홈페이지의 예제를 그대로 참고한 것으로 이해하기에 난해한 부분이 있습니다. 그래서 굳이 넘쳐나는 자료들 속에 다시 키보드를 잡아들었습니다. 아무튼 이번 글이 누군가에게 현실적인 도움이 되기를 바랍니다.

Flask 코드 작성

우선 unittest를 위해 아래와 같은 코드를 작성해보도록 합시다.

# contents of my_flask.py
from flask import Flask
app = Flask(__name__)

@app.route("/hello")
def hello():
    return "Hello!"

@app.route("/world")
def world():
    return "World!"

if __name__ == '__main__':
    app.run(debug=True)

위와같이 /hello와 /world의 GET 방식에 대해서 각각 처리하는 API를 만들었을 때 이 두 메소드를 테스트하는 코드를 작성하는것으로 unittest를 시작하고 앞으로 더 복잡한 코드를 다뤄보도록 합니다. 일단 위에서 작성한 코드를 실행할 수 있도록 flask를 설치하고 다음 단계로 넘어가도록 합시다.

pip install Flask

pytest 예제코드 테스트

unittest를 위해 여기서는 pytest를 사용하도록 합니다. 다른 테스트 방법들도 있겠지만 pytest를 선택한 이유는 Flask 공식페이지에서도 unittest로 pytest를 사용하고 있기 때문입니다. 먼저 pytest 모듈을 설치하도록 합시다.

pip install pytest

pytest의 동작원리를 알기 위해서 pytest의 공식페이지에 있는 예제를 참고해보도록 합시다.

# content of test_sample.py
def inc(x):
    return x + 1

def test_answer():
    assert inc(3) == 5

위에 코드를 잠시 살펴봤을 때 test_answer() 함수가 호출되면 "inc(3) == 5" 구문이 True가 아니기 때문에 AssertionError 가 발생합니다. pytest는 현재 경로부터 하위에 존재하는 모든 디렉터리를 순회하며 모든 test_*py을 실행하고 찾은 파일 안에 존재하는 모든 test*() 함수를 실행합니다. 그렇기 때문에 터미널에서 pytest를 실행하면 test_sample.py가 실행될 것이고 test_answer()함수가 차례로 호출 될 것입니다. 그렇다면 이제 터미널에서 pytest를 실행해보도록 합시다.

$ pytest
============================== test session starts ==============================
platform darwin -- Python 3.7.1, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: /Users/jybaek/work/10_study/flask, inifile:
plugins: remotedata-0.3.1, openfiles-0.3.1, doctestplus-0.2.0, arraydiff-0.3
collected 1 item

test_sample.py F                                                          [100%]

=================================== FAILURES ====================================
__________________________________ test_answer __________________________________

    def test_answer():
>       assert inc(3) == 5
E       assert 4 == 5
E        +  where 4 = inc(3)

test_sample.py:6: AssertionError
=========================== 1 failed in 0.04 seconds ============================

우리가 예상한것처럼 test_sample.py가 실행되었고, test_answer()함수에서 AssertionError 가 발생했습니다. 이제 pytest에 대한 기본 이해를 마쳤으니 이를 flask에 적용시켜보도록 합시다.

unittest 코드작성

/hello와 /world를 테스트하는 unittest 코드를 아래와 같이 작성해봅시다.

# content of test_flask.py
from my_flask import hello, world
def test_hello():
    rv = hello()
    assert "Hello!" == rv

def test_world():
    rv = world()
    assert "World" == rv

작성한 코드를 pytest를 통해 바로 확인해보도록 합시다. 우리가 무엇을 하고 있는지 다시 이야기해보자면, my_flask.py라는 API 앱을 만들었고, 이 코드를 테스트하기 위한 코드로 test_flask.py를 작성했습니다. 그리고 이제 pytest를 통해 작성한 test_flask.py의 내용을 돌려보는 것입니다.

$ pytest
============================== test session starts ==============================
platform darwin -- Python 3.7.1, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: /Users/jybaek/work/10_study/flask, inifile:
plugins: remotedata-0.3.1, openfiles-0.3.1, doctestplus-0.2.0, arraydiff-0.3
collected 2 items

test_flask.py .F                                                          [100%]

=================================== FAILURES ====================================
__________________________________ test_world ___________________________________

    def test_world():
        rv = world()
>       assert "World" == rv
E       AssertionError: assert 'World' == 'World!'
E         - World
E         + World!
E         ?      +

test_flask.py:9: AssertionError
====================== 1 failed, 1 passed in 0.11 seconds =======================

2개의 테스트 결과는 1개 성공, 1개 실패로 출력되었습니다. 위에 100%가 출력되어 있는 좌측에 test_flask.py .F 가 보이는데 .(dot)은 성공을 의미하고 F는 실패를 의미한합니다. 즉, 두번째 테스트 케이스가 실패했다는 것입니다. pytest에 -v (vervose) 옵션을 주면 조금 더 보기 편합니다. 아래 -v 옵션을 줬을 때의 결과를 살펴봅시다.

$ pytest -v
============================== test session starts ==============================
platform darwin -- Python 3.7.1, pytest-4.0.2, py-1.7.0, pluggy-0.8.0 -- /anaconda3/bin/python
cachedir: .pytest_cache
rootdir: /Users/jybaek/work/10_study/flask, inifile:
plugins: remotedata-0.3.1, openfiles-0.3.1, doctestplus-0.2.0, arraydiff-0.3
collected 2 items

test_flask.py::test_hello PASSED                                          [ 50%]
test_flask.py::test_world FAILED                                          [100%]

=================================== FAILURES ====================================
__________________________________ test_world ___________________________________

    def test_world():
        rv = world()
>       assert "World" == rv
E       AssertionError: assert 'World' == 'World!'
E         - World
E         + World!
E         ?      +

test_flask.py:9: AssertionError
====================== 1 failed, 1 passed in 0.12 seconds =======================

중간에 어떤 함수들이 실행되었는지 한눈에 확인되서 조금은 더 보기 편합니다.

test_flask.py::test_hello PASSED                                          [ 50%]
test_flask.py::test_world FAILED                                          [100%]

다시 본론으로 돌아와서, 테스트 케이스가 실패한 곳을 살펴보면 rv에는 "World!"라는 string이 들어있는데 "World"와 비교하니 당연히 AssertionError가 발생합니다. (느낌표를 일부러 빠뜨렸습니다.)

 

여기까지가 unittest의 굉장히 심플한 테스트 과정이었고 지금까지 이해한 것을 바탕으로 다음편에서 조금 더 현실적이고 복잡한 예제를 다뤄보도록 합시다.

 

이어지는글 : 다시쓰는 Flask unittest (하편)

댓글
댓글쓰기 폼