Flask unit test ๐Ÿ”ฅ ์œ ๋‹› ํ…Œ์ŠคํŠธ

matisseยท2020๋…„ 7์›” 9์ผ
0

์ถœ์ฒ˜:
๊ณต์‹ ๋ฌธ์„œ, flask ์„œ๋ฒ„์— unittest ์ ์šฉํ•˜๊ธฐ, ๊น”ํŒŒํƒ„๋ฐฑ ์ฐธ๊ณ ํ•œ ๋ธ”๋กœ๊ทธ
๊ณต์‹๋ฌธ์„œ2 + ๊ณต์‹repo


django์—์„œ๋Š” unit test์˜ ๊ฐœ๋…๋งŒ ์ดํ•ดํ•œ๋‹ค๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋Š” ๊ฒƒ์ด ์–ด๋ ต์ง€ ์•Š์•˜๋‹ค. ORM์ด ์•Œ์•„์„œ ๋ฐ์ดํ„ฐ ๋ถ€๋ถ„์„ ์ž˜ ์ฒ˜๋ฆฌํ•ด์คฌ๊ธฐ ๋•Œ๋ฌธ์—, ๋‚˜๋Š” ๊ทธ์ € ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๊ณ  ์ง€์šฐ๊ธฐ๋งŒ ํ•˜๋ฉด ๋๋‹ค.

ํ•˜์ง€๋งŒ flask์˜ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค connection์„ ์ง์ ‘ ํ•˜๋‹ค๋ณด๋‹ˆ ๋ดค์„ ๋•Œ ๋ฐ”๋กœ ์ดํ•ด๊ฐ€ ๊ฐ€์ง€ ์•Š์•˜๋‹ค. ๊ฐ“ ORM์ด ๋‚  ์œ„ํ•ด ๋’ค์—์„œ ๋ญ˜ ํ•ด์ฃผ๊ณ  ์žˆ์—ˆ๋Š”์ง€ ๋ชฐ๋ž๊ธฐ ๋•Œ๋ฌธ์—(...) ๋” ๊ทธ๋žฌ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.

์šฐ์„  ๊น”ํŒŒํƒ„๋ฐฑ์„ ์ฐธ๊ณ ํ•œ ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐธ๊ณ ํ–ˆ๊ณ , django์—์„œ python ๋‚ด์žฅ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•œ ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ ์ด๋ฒˆ์—๋Š” pytest๋ฅผ ์จ๋ณด์•˜๋‹ค.

file ๊ตฌ์กฐ

flaks ๊ณต์‹ repo๋ฅผ ๋ณด๋‹ˆ, ๋ฃจํŠธ์— test ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ทธ ์•ˆ์— ๊ฐ ์•ฑ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜๋„๋ก ๋˜์–ด์žˆ์—ˆ๋‹ค. unit test๋ฅผ ์ž‘์„ฑํ•œ ํŒŒ์ผ์˜ ๊ฒฝ์šฐ ๋ฐ˜๋“œ์‹œ test๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค. ์•„๋ž˜ ์ด๋ฏธ์ง€๋ฅผ ๋ณด๋ฉด ํŒŒ์ผ ์ด ๋‹ค test ์–ธ๋”๋ฐ”๋กœ ์‹œ์ž‘๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

test database ์ƒ์„ฑ

๋จผ์ € ์•ˆ์ „ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๊ด€๋ จ ์ •๋ณด๋ฅผ ๋‹ด์€ config.py์— test ์œ ์ €๋ฅผ ๋งŒ๋“ค๊ณ , mysql connector๋กœ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—ฐ๊ฒฐํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๊ฒƒ์€ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์Šคํ‚ค๋งˆ๊ฐ€ ๋™๊ธฐํ™”๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค.

  test_db = {
  'user': 'test',
  'password': '1234',
  'host': 'localhost',
  'port': 3306,
  'database': 'test_db'
  }
	
  test_config = {
      "DB_URL": f"mysql+mysqlconnector://{test_db['user']}
      :{test_db['password']}@{test_db['host']}
      :{test_db['port']}
      /{test_db['database']}?charset=utf8"
  }

๋ฆฌํ„ด๊ฐ’ ๋ฐ›๊ธฐ

flask์—์„œ๋Š” ๋‚ด๊ฐ€ ๋งŒ๋“  ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” test_client๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. pytest.fixture ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ์—”๋“œํฌ์ธํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํ•จ์ˆ˜์˜ ๋ฆฌํ„ด ๊ฐ’์„ ์ธ์ž๋กœ ๋„ฃ์–ด์ฃผ๊ฒŒ ๋œ๋‹ค.

  @pytest.fixture
  def api():
      app = create_app(config.test_config)
      app.config['TESTING'] = True # unit test์—์„œ ์—๋Ÿฌ๋ฅผ ์บ์น˜ํ•˜๊ธฐ ์œ„ํ•ด, ์˜ˆ์™ธ๊ฐ€ ํ•ธ๋“ค๋ง ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ•ด์ฃผ๋Š” ๊ฒƒ 
      api = app.test_client()
	
      return api

You must set app.testing = True in order for the exceptions to propagate to the test client. Otherwise, the exception will be handled by the application (not visible to the test client) and the only indication of an AssertionError or other exception will be a 500 status code response to the test client.

test

๊น”ํŒŒํƒ„๋ฐฑ ์ฑ…์—์„œ ๋ฏธ๋‹ˆํ„ฐ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋˜๋Š”๋ฐ, ์•„๋ž˜๋Š” ๊ทธ ๋ฏธ๋‹ˆํ„ฐ์—์„œ ํŠธ์œ—์„ ์ƒ์„ฑํ•˜๋Š” api์˜ test ์ฝ”๋“œ๋‹ค.

  • ์œ„์— ์ฝ”๋“œ๊ฐ€ ๋”์žˆ๋Š”๋ฐ access_token๊ณผ new_user_id์˜ ๊ฒฝ์šฐ ์œ„์—์„œ ํšŒ์›์„ ์ƒ์„ฑํ•˜๋ฉด์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋‚ด์—์„œ ๋งŒ๋“ค์–ด์ง„ ๋ณ€์ˆ˜๋‹ค. ์˜์กด์„ฑ์ด ์žˆ๋Š” ์ฝ”๋“œ์ง€๋งŒ post, get์ด ์–ด๋–ค ํ˜•์‹์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€๋งŒ ๋ณด๋ ค๊ณ  ํ•˜๋‚˜์”ฉ๋งŒ ๊ฐ€์ ธ์™”๋‹ค)

๊ธฐ๋ณธ์ ์ธ ๋™์ž‘ ์›๋ฆฌ๋Š” ๋‚ด๊ฐ€ ๋งŒ๋“  ์—”๋“œํฌ์ธํŠธ์˜ return ๊ฐ’๊ณผ test db์˜ return ๊ฐ’์„ ๋งŒ๋“  ๊ฒƒ์ด ์ผ์น˜ํ•˜๋Š” ์ง€๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ฒซ ๋ฒˆ์งธ(#tweet)๋Š” post method๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ด๊ณ , db์ž…๋ ค ๊ฐ’์ธ data์™€ content_type, header๋ฅผ ๋งŒ๋“ค์–ด resp(response) ๋ณ€์ˆ˜์— ์ €์žฅํ•œ๋‹ค. ์ด๋•Œ endpoint /tweet์˜ response์™€ ๋‚ด๊ฐ€ assert์—์„œ ๊ฐ€์ •ํ•œ status code๊ฐ€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ฒŒ ๋œ๋‹ค.

๋‘ ๋ฒˆ์งธ(#tweet check)๋Š” get method ํ…Œ์ŠคํŠธ๋‹ค. timeline/1 ํ˜•์‹์˜ endpoint์—์„œ response๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , json ํ˜•์‹์œผ๋กœ ๋งŒ๋“ ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚ด๊ฐ€ ์‹ค์ œ db๋ฅผ ํ†ตํ•ด ๋ฐ›์•„์•ผ ํ•˜๋Š” ๊ฐ’๊ณผ ์ฝ”๋“œ๋ฅผ assertํ•˜๊ฒŒ ๋˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

 # tweet
      resp = api.post(
          "/tweet",
          data=json.dumps({"tweet": "Hello World"}),
          content_type="application/json",
          headers={"Authorization": access_token}
      )
      assert resp.status_code == 200
	
      # tweet check
      resp = api.get(f"/timeline/{new_user_id}")
      tweets = json.loads(resp.data.decode("utf-8"))
	
      assert resp.status_code == 200
      assert tweets == {
          "user_id": 1,
          "timeline" : [
              {
                  "user_id": 1,
                  "tweet": "Hello World"
              }
          ]
      }

0๊ฐœ์˜ ๋Œ“๊ธ€