๐ ์ด ๊ธ์ ์ด๋ฐ ๋ถ๊ป ์ถ์ฒํฉ๋๋ค
- Python๊ณผ requests๋ก POST ์์ฒญ ํ ์คํธ๋ฅผ ์์ฑํ๊ณ ์ถ์ ๋ถ
- API ํ ์คํธ์์ ๋ฐ์ดํฐ ์์ฑ ๊ฒ์ฆ ๋ฐฉ๋ฒ์ ๋ฐฐ์ฐ๊ณ ์ถ์ QA ์์ง๋์ด
- ์ ์๊ณใป์ ํจ์ฑ ๊ฒ์ฌใป์ด์๊ณ POST ํ ์คํธ๋ฅผ ๊ตฌํํ๊ณ ์ถ์ ๋ถ
- pytest๋ก API ํ ์คํธ๋ฅผ ์๋ํํ์ฌ ํฌํธํด๋ฆฌ์ค์ ํ์ฉํ๊ณ ์ถ์ ๋ถ
โ ์ด ๊ธ์ ์ฝ์ผ๋ฉด ์ ์ ์๋ ๊ฒ
- pytest์ requests๋ก POST ์์ฒญ API ํ ์คํธ๋ฅผ ์์ฑํ๋ ๊ธฐ๋ณธ ํจํด
- ๋ฐ์ดํฐ ์์ฑ ํ ์๋ต ๊ฒ์ฆ๏ผ์ํใป๋ฐ๋ใปํค๋๏ผ๋ฐฉ๋ฒ
- ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ์ ์ด์๊ณ ํ ์คํธ ๊ตฌํ ๋ฐฉ๋ฒ
- raise_for_status()๋ฅผ ์ฌ์ฉํ ์ค๋ฌด ์์ค์ ํ ์คํธ ์ฝ๋ ์์ฑ๋ฒ
Python๊ณผ pytest๋ก API ํ ์คํธ๋ฅผ ์์ฑํ ๋ GET ๋ค์์ผ๋ก ๊ตฌํํ๋ ๊ฒ์ด POST ์์ฒญ ํ ์คํธ์ ๋๋ค. pytest์ requests๋ฅผ ์ฌ์ฉํด์ ใ๋ฐ์ดํฐ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์์ฑ๋๋๊ฐ๏ผใใ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฐํ๋๋๊ฐ๏ผใ๋ฅผ ์๋์ผ๋ก ๊ฒ์ฆํ๋ ๋ฐฉ๋ฒ์ ํด์คํฉ๋๋ค.
์ด ๊ธ์์๋ POST ์์ฒญ์ ๊ธฐ๋ณธ๋ถํฐ ์ด์๊ณใป์ ํจ์ฑ ๊ฒ์ฌ ๊ฒ์ฆ๊น์ง๋ฅผ ์ค๋ฌด์์ ์ฌ์ฉํ ์ ์๋ ์ฝ๋์ ํจ๊ป ์๊ฐํฉ๋๋ค.
- 00. API ํ ์คํธ์ POST ์์ฒญ์ ๊ธฐ๋ณธ
- 01. pytest + requests์ API ํ ์คํธ ํ๊ฒฝ
- 02. Python์ผ๋ก API POST ํ ์คํธ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ
- 03. API ํ ์คํธ POST ์ ํจ์ฑ ๊ฒ์ฌ ๊ฒ์ฆ
- 04. API ํ ์คํธ POST ์คํค๋ง ๊ฒ์ฆ
- 05. POST ํ ์คํธ ์ ์ฒด ์ฝ๋
- 06. ์์ฃผ ๊ฒช๋ ๋ฌธ์ & ํด๊ฒฐ๋ฒ
- 07. ์์ฃผ ๋ฌป๋ ์ง๋ฌธ๏ผFAQ๏ผ
- 08. ์ ๋ฆฌ
00. API ํ ์คํธ์ POST ์์ฒญ์ ๊ธฐ๋ณธ
POST ์์ฒญ์ ใ๋ฐ์ดํฐ๋ฅผ ์๋ก ์์ฑํ๋ใ ์กฐ์์ ๋๋ค. GET๊ณผ ๋ฌ๋ฆฌ ์์ฒญ ๋ฐ๋์ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ์ฌ ์๋ฒ์ ์ ์กํฉ๋๋ค.
POST ํ ์คํธ์์๋ ์ฃผ๋ก ๋ค์์ ๊ฒ์ฆํฉ๋๋ค.
| ํ์ธ ํญ๋ชฉ | ๋ด์ฉ | ์ |
|---|---|---|
| ์ํ ์ฝ๋ | ์์ฑ ์ฑ๊ณต ์ 201์ด ๋ฐํ๋๋๊ฐ | 201 Created |
| ์๋ต ๋ฐ๋ | ์ ์กํ ๋ฐ์ดํฐ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฐํ๋๋๊ฐ | name๊ณผ email ๊ฐ |
| ID ๋ถ์ฌ | ์๋ก ์์ฑ๋ ๋ฆฌ์์ค์ ID๊ฐ ๋ถ์ฌ๋์ด ์๋๊ฐ | “id”: 11 |
| ์ ํจ์ฑ ๊ฒ์ฌ | ๋ถ์ ํ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋์ ๋ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋๋๊ฐ | 400 Bad Request |
๐ก ํฌ์ธํธ๏ผPOST ํ ์คํธ๋ GET๋ณด๋ค ๊ฒ์ฆ ํญ๋ชฉ์ด ๋ง์ต๋๋ค. ์ ์๊ณ๋ฟ๋ง ์๋๋ผ ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ์ ์ด์๊ณ๋ ๋ฐ๋์ ํ ์คํธํจ์ผ๋ก์จ API ํ์ง์ ๋ด๋ณดํ ์ ์์ต๋๋ค.
01. pytest + requests์ API ํ ์คํธ ํ๊ฒฝ
์์ง ํ๊ฒฝ ๊ตฌ์ถ์ด ์ ๋ ๋ถ์ ๋จผ์ ์ค์นํด์ฃผ์ธ์.
pip install requests pytest pytest-html์ด๋ฒ ํ
์คํธ ๋์์ ๋ฌด๋ฃ ๋ชฉ๏ผmock๏ผ API JSONPlaceholder ์
๋๋ค.
POST https://jsonplaceholder.typicode.com/users์ ์กํ๋ ์์ฒญ ๋ฐ๋ ์ํ์ ๋๋ค.
new_user = {
"name": "Yoshitsugu Tester",
"username": "yoshitsugu728",
"email": "yoshitsugu@example.com",
"phone": "090-1234-5678",
"website": "qa-auto-lab.com"
}๐ก ํฌ์ธํธ๏ผJSONPlaceholder๋ ๋ชฉ API์ด๋ฏ๋ก ์ค์ ๋ก๋ ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋์ง ์์ต๋๋ค. ํ์ง๋ง ์๋ต์ ์ค์ API์ ๋์ผํ ํ์์ผ๋ก ๋ฐํ๋๋ฏ๋ก ํ ์คํธ ํ์ต์ ์ต์ ์ ๋๋ค.
02. Python์ผ๋ก API POST ํ ์คํธ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ
๊ธฐ๋ณธ์ ์ธ POST ํ ์คํธ๏ผ201 ํ์ธ๏ผ
POST ์์ฒญ์ ๋ณด๋ด๊ณ 201 Created๊ฐ ๋ฐํ๋๋ ๊ฒ์ ํ์ธํฉ๋๋ค.
import requests
BASE_URL = "https://jsonplaceholder.typicode.com"
def test_post_user_status_code():
"""TC01: ๋ฐ์ดํฐ ์์ฑ ์ ์ํ ์ฝ๋ 201์ด ๋ฐํ๋์ด์ผ ํ๋ค"""
new_user = {
"name": "Yoshitsugu Tester",
"username": "yoshitsugu728",
"email": "yoshitsugu@example.com"
}
response = requests.post(f"{BASE_URL}/users", json=new_user)
assert response.status_code == 201, \
f"๊ธฐ๋๊ฐ: 201, ์ค์ ๊ฐ: {response.status_code}"
print(f"\nโ
TC01 PASS | ์ํ ์ฝ๋: {response.status_code}")raise_for_status()๋ก 4xx/5xx๋ฅผ ํ์คํ ๊ฒ์ง
def test_post_user_raise_for_status():
"""TC02: 4xx/5xx ์ค๋ฅ ์ ์์ธ๊ฐ ๋ฐ์ํด์ผ ํ๋ค"""
new_user = {"name": "Test User", "email": "test@example.com"}
response = requests.post(f"{BASE_URL}/users", json=new_user)
# 4xx/5xx ๋ ์๋์ผ๋ก HTTPError ๋ฐ์
response.raise_for_status()
assert response.status_code == 201
print(f"\nโ
TC02 PASS | raise_for_status() ์ ์ ํต๊ณผ")๐ก ํฌ์ธํธ๏ผresponse.raise_for_status() ๋ ์ค๋ฌด์์ ํ์ 1ํ์
๋๋ค. POST ํ
์คํธ์์๋ ๋ฐ๋์ ์ถ๊ฐํด๋์ธ์.
ํค๋ ๊ฒ์ฆ
def test_post_user_header():
"""TC03: Content-Type์ด JSON์ด์ด์ผ ํ๋ค"""
new_user = {"name": "Test User", "email": "test@example.com"}
response = requests.post(f"{BASE_URL}/users", json=new_user)
response.raise_for_status()
content_type = response.headers.get("Content-Type", "")
assert "application/json" in content_type, \
f"์์ ๋ฐ์ Content-Type: {content_type}"
print(f"\nโ
TC03 PASS | Content-Type: {content_type}")๐ก ํฌ์ธํธ๏ผContent-Type์ application/json; charset=utf-8 ํํ๋ก ๋ฐํ๋๋ฏ๋ก == ์์ ์ผ์น๊ฐ ์๋ in ๋ถ๋ถ ์ผ์น๋ก ๊ฒ์ฆํฉ๋๋ค.
์๋ต ๋ฐ๋ ๊ฒ์ฆ
์ ์กํ ๋ฐ์ดํฐ๊ฐ ์๋ต์ ์ฌ๋ฐ๋ฅด๊ฒ ํฌํจ๋์ด ์๋์ง ํ์ธํฉ๋๋ค.
def test_post_user_response_body():
"""TC04: ์ ์กํ ๋ฐ์ดํฐ๊ฐ ์๋ต์ ์ฌ๋ฐ๋ฅด๊ฒ ํฌํจ๋์ด์ผ ํ๋ค"""
new_user = {
"name": "Yoshitsugu Tester",
"username": "yoshitsugu728",
"email": "yoshitsugu@example.com"
}
response = requests.post(f"{BASE_URL}/users", json=new_user)
response.raise_for_status()
body = response.json()
assert body["name"] == new_user["name"], f"name ๋ถ์ผ์น: {body['name']}"
assert body["email"] == new_user["email"], f"email ๋ถ์ผ์น: {body['email']}"
print(f"\nโ
TC04 PASS | ์์ฑ๋ ์ฌ์ฉ์: {body['name']}")ID๊ฐ ๋ถ์ฌ๋์๋์ง ํ์ธ
์๋ก ์์ฑ๋ ๋ฆฌ์์ค์ ์๋ฒ๋ก๋ถํฐ ID๊ฐ ๋ถ์ฌ๋์๋์ง ํ์ธํฉ๋๋ค.
def test_post_user_id_assigned():
"""TC05: ์์ฑ๋ ๋ฆฌ์์ค์ ID๊ฐ ๋ถ์ฌ๋์ด์ผ ํ๋ค"""
new_user = {"name": "Test User", "email": "test@example.com"}
response = requests.post(f"{BASE_URL}/users", json=new_user)
response.raise_for_status()
body = response.json()
assert "id" in body, "์๋ต์ id๊ฐ ํฌํจ๋์ด ์์ง ์์ต๋๋ค"
assert isinstance(body["id"], int), f"id๋ intํ์ด ๊ธฐ๋๊ฐ: {type(body['id'])}"
assert body["id"] > 0, f"id๋ ์์ ์ ์๊ฐ ๊ธฐ๋๊ฐ: {body['id']}"
print(f"\nโ
TC05 PASS | ๋ถ์ฌ๋ ID: {body['id']}")๐ก ํฌ์ธํธ๏ผID ๋ถ์ฌ ํ์ธ์ ์ค๋ฌด์์ ์์ฃผ ๊ฐ๊ณผ๋๋ ํ ์คํธ์ ๋๋ค. ์๋ฒ๊ฐ ID๋ฅผ ์๋ ์ฑ๋ฒํ๊ณ ์๋์ง, ํ์ ์ ์ฌ๋ฐ๋ฅธ์ง ํจ๊ป ํ์ธํ์ธ์.
03. API ํ ์คํธ POST ์ ํจ์ฑ ๊ฒ์ฌ ๊ฒ์ฆ
์ค๋ฌด์์๋ ์ ์๊ณ๋ฟ๋ง ์๋๋ผ ๋ถ์ ํ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋์ ๋ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋๋์ง๋ ๋ฐ๋์ ๊ฒ์ฆํฉ๋๋ค.
ํ์ ํ๋ ์์ด ์ ์ก๏ผ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ ํ์ธ๏ผ
def test_post_user_missing_required_fields():
"""TC06: ํ์ ํ๋ ์์ด ์ ์กํ์ ๋ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค"""
incomplete_user = {
"username": "yoshitsugu728"
# name๊ณผ email์ ์๋์ ์ผ๋ก ์๋ต
}
response = requests.post(f"{BASE_URL}/users", json=incomplete_user)
# JSONPlaceholder๋ ๋ชฉ์ด๋ฏ๋ก 201์ ๋ฐํ
# ์ค์ API์์๋ 400 Bad Request๊ฐ ๊ธฐ๋๋จ
assert response.status_code in [201, 400], \
f"๊ธฐ๋๊ฐ: 201 ๋๋ 400, ์ค์ ๊ฐ: {response.status_code}"
print(f"\nโ
TC06 PASS | ํ์ ํ๋ ์์ ๋ ์ํ: {response.status_code}")๋น ๋ฌธ์์ด์ ์ ์ก
def test_post_user_empty_string():
"""TC07: ๋น ๋ฌธ์์ด์ ์ ์กํ์ ๋ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค"""
invalid_user = {
"name": "", # ๋น ๋ฌธ์์ด
"email": "" # ๋น ๋ฌธ์์ด
}
response = requests.post(f"{BASE_URL}/users", json=invalid_user)
assert response.status_code in [201, 400, 422], \
f"์์ ๋ฐ์ ์ํ ์ฝ๋: {response.status_code}"
print(f"\nโ
TC07 PASS | ๋น ๋ฌธ์์ด ์ ์ก ์ ์ํ: {response.status_code}")์๋ชป๋ ์ด๋ฉ์ผ ํ์์ ์ ์ก
def test_post_user_invalid_email():
"""TC08: ์๋ชป๋ ์ด๋ฉ์ผ ํ์์ ์ ์กํ์ ๋ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค"""
invalid_user = {
"name": "Test User",
"email": "not-an-email" # ์ด๋ฉ์ผ ํ์์ด ์๋
}
response = requests.post(f"{BASE_URL}/users", json=invalid_user)
assert response.status_code in [201, 400, 422], \
f"์์ ๋ฐ์ ์ํ ์ฝ๋: {response.status_code}"
print(f"\nโ
TC08 PASS | ์๋ชป๋ ์ด๋ฉ์ผ ์ ์ก ์ ์ํ: {response.status_code}")โ ๏ธ ์ฃผ์๏ผJSONPlaceholder๋ ๋ชฉ API์ด๋ฏ๋ก ๋ถ์ ํ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ 201์ด ๋ฐํ๋ฉ๋๋ค. ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ ํํ๊ฒ ํ ์คํธํ๋ ค๋ฉด ์ค์ ์ ํจ์ฑ ๊ฒ์ฌ ๊ธฐ๋ฅ์ ๊ฐ์ง API๊ฐ ํ์ํฉ๋๋ค. Restful Booker๋ reqres.in ๋ฑ์ ์ฌ์ฉํ๊ฑฐ๋ ์ง์ ๋ง๋ API๋ก ๊ฒ์ฆํ์ธ์.
04. API ํ ์คํธ POST ์คํค๋ง ๊ฒ์ฆ
์์ฑ๋ ๋ฆฌ์์ค์ ์๋ต์ด ์ฌ๋ฐ๋ฅธ ํ์ ์ธ์ง๋ ํ์ธํฉ๋๋ค.
def test_post_user_schema():
"""TC09: ์๋ต ์คํค๋ง๏ผํ์
๏ผ๊ฐ ์ฌ๋ฐ๋ฅด๋ค"""
new_user = {
"name": "Yoshitsugu Tester",
"email": "yoshitsugu@example.com"
}
response = requests.post(f"{BASE_URL}/users", json=new_user)
response.raise_for_status()
body = response.json()
assert isinstance(body["id"], int), f"id๋ intํ์ด ๊ธฐ๋๊ฐ: {type(body['id'])}"
assert isinstance(body["name"], str), f"name์ strํ์ด ๊ธฐ๋๊ฐ: {type(body['name'])}"
print(f"\nโ
TC09 PASS | ์คํค๋ง ๊ฒ์ฆ ์๋ฃ")05. POST ํ ์คํธ ์ ์ฒด ์ฝ๋
"""
POST API Test
Target: JSONPlaceholder (https://jsonplaceholder.typicode.com)
Framework: Python + requests + pytest
"""
import requests
BASE_URL = "https://jsonplaceholder.typicode.com"
def test_post_user_status_code():
"""TC01: ๋ฐ์ดํฐ ์์ฑ ์ ์ํ ์ฝ๋ 201์ด ๋ฐํ๋์ด์ผ ํ๋ค"""
new_user = {"name": "Yoshitsugu Tester", "email": "yoshitsugu@example.com"}
response = requests.post(f"{BASE_URL}/users", json=new_user)
assert response.status_code == 201, \
f"๊ธฐ๋๊ฐ: 201, ์ค์ ๊ฐ: {response.status_code}"
print(f"\nโ
TC01 PASS | status: {response.status_code}")
def test_post_user_raise_for_status():
"""TC02: 4xx/5xx ์ค๋ฅ ์ ์์ธ๊ฐ ๋ฐ์ํด์ผ ํ๋ค"""
new_user = {"name": "Test User", "email": "test@example.com"}
response = requests.post(f"{BASE_URL}/users", json=new_user)
response.raise_for_status()
assert response.status_code == 201
print(f"\nโ
TC02 PASS | raise_for_status() ์ ์ ํต๊ณผ")
def test_post_user_header():
"""TC03: Content-Type์ด JSON์ด์ด์ผ ํ๋ค"""
new_user = {"name": "Test User", "email": "test@example.com"}
response = requests.post(f"{BASE_URL}/users", json=new_user)
response.raise_for_status()
content_type = response.headers.get("Content-Type", "")
assert "application/json" in content_type, \
f"์์ ๋ฐ์ Content-Type: {content_type}"
print(f"\nโ
TC03 PASS | Content-Type: {content_type}")
def test_post_user_response_body():
"""TC04: ์ ์กํ ๋ฐ์ดํฐ๊ฐ ์๋ต์ ์ฌ๋ฐ๋ฅด๊ฒ ํฌํจ๋์ด์ผ ํ๋ค"""
new_user = {"name": "Yoshitsugu Tester", "email": "yoshitsugu@example.com"}
response = requests.post(f"{BASE_URL}/users", json=new_user)
response.raise_for_status()
body = response.json()
assert body["name"] == new_user["name"]
assert body["email"] == new_user["email"]
print(f"\nโ
TC04 PASS | name: {body['name']}")
def test_post_user_id_assigned():
"""TC05: ์์ฑ๋ ๋ฆฌ์์ค์ ID๊ฐ ๋ถ์ฌ๋์ด์ผ ํ๋ค"""
new_user = {"name": "Test User", "email": "test@example.com"}
response = requests.post(f"{BASE_URL}/users", json=new_user)
response.raise_for_status()
body = response.json()
assert "id" in body
assert isinstance(body["id"], int)
assert body["id"] > 0
print(f"\nโ
TC05 PASS | ๋ถ์ฌ๋ ID: {body['id']}")
def test_post_user_missing_required_fields():
"""TC06: ํ์ ํ๋ ์์ด ์ ์กํ์ ๋ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค"""
incomplete_user = {"username": "yoshitsugu728"}
response = requests.post(f"{BASE_URL}/users", json=incomplete_user)
assert response.status_code in [201, 400]
print(f"\nโ
TC06 PASS | status: {response.status_code}")
def test_post_user_empty_string():
"""TC07: ๋น ๋ฌธ์์ด์ ์ ์กํ์ ๋ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค"""
invalid_user = {"name": "", "email": ""}
response = requests.post(f"{BASE_URL}/users", json=invalid_user)
assert response.status_code in [201, 400, 422]
print(f"\nโ
TC07 PASS | status: {response.status_code}")
def test_post_user_invalid_email():
"""TC08: ์๋ชป๋ ์ด๋ฉ์ผ ํ์์ ์ ์กํ์ ๋ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค"""
invalid_user = {"name": "Test User", "email": "not-an-email"}
response = requests.post(f"{BASE_URL}/users", json=invalid_user)
assert response.status_code in [201, 400, 422]
print(f"\nโ
TC08 PASS | status: {response.status_code}")
def test_post_user_schema():
"""TC09: ์๋ต ์คํค๋ง๏ผํ์
๏ผ๊ฐ ์ฌ๋ฐ๋ฅด๋ค"""
new_user = {"name": "Yoshitsugu Tester", "email": "yoshitsugu@example.com"}
response = requests.post(f"{BASE_URL}/users", json=new_user)
response.raise_for_status()
body = response.json()
assert isinstance(body["id"], int)
assert isinstance(body["name"], str)
print(f"\nโ
TC09 PASS | ์คํค๋ง ๊ฒ์ฆ ์๋ฃ")์คํ ์ปค๋งจ๋
pytest test_post_api.py -v -s์คํ ๊ฒฐ๊ณผ ์ํ
test_post_api.py::test_post_user_status_code PASSED
test_post_api.py::test_post_user_raise_for_status PASSED
test_post_api.py::test_post_user_header PASSED
test_post_api.py::test_post_user_response_body PASSED
test_post_api.py::test_post_user_id_assigned PASSED
test_post_api.py::test_post_user_missing_required_fields PASSED
test_post_api.py::test_post_user_empty_string PASSED
test_post_api.py::test_post_user_invalid_email PASSED
test_post_api.py::test_post_user_schema PASSED
9 passed in 4.12s โ
06. ์์ฃผ ๊ฒช๋ ๋ฌธ์ & ํด๊ฒฐ๋ฒ
๊ตฌํ ์ค ์ค์ ๋ก ๊ฒช์๋ ๋ฌธ์ ๋ค์ ์ ๋ฆฌํ์ต๋๋ค. ๊ฐ์ ๊ณณ์์ ๋งํ๋ ๋ถ๋ค๊ป ๋์์ด ๋๋ฉด ์ข๊ฒ ์ต๋๋ค.
โ json=๊ณผ data=์ ์ฐจ์ด๋ก ๋งํ๋ค
requests๋ก POST ์์ฒญ์ ๋ณด๋ผ ๋ json= ๊ณผ data= ์ ์ฐจ์ด๋ฅผ ๋ชฐ๋ผ์ ๋งํ์ต๋๋ค. data= ๋ฅผ ์ฌ์ฉํ๋ฉด Content-Type์ด application/x-www-form-urlencoded ๊ฐ ๋์ด๋ฒ๋ ค JSON API์์ ์คํจํฉ๋๋ค.
# โ data=๋ฅผ ์ฌ์ฉํ๋ฉด JSON ํ์์ผ๋ก ์ ์ก๋์ง ์์
response = requests.post(url, data={"name": "Taro"})
# โ
json=๋ฅผ ์ฌ์ฉํ๋ฉด ์๋์ผ๋ก Content-Type: application/json์ด ์ค์ ๋จ
response = requests.post(url, json={"name": "Taro"})๐ก ํฌ์ธํธ๏ผjson= ์ ์ฌ์ฉํ๋ฉด ์์ฒญ ๋ฐ๋ ๋ณํ๊ณผ Content-Type ์ค์ ์ด ์๋์ผ๋ก ์ด๋ฃจ์ด์ง๋ฏ๋ก JSON API์๋ ๋ฐ๋์ json= ์ ์ฌ์ฉํ์ธ์.
โก POST ์ํ ์ฝ๋๊ฐ 200์ธ์ง 201์ธ์ง ํท๊ฐ๋ฆฐ๋ค
POST ์ฑ๊ณต ์๋ต์ด 200์ธ์ง 201์ธ์ง ํท๊ฐ๋ ธ์ต๋๋ค. API ์ค๊ณ์ ๋ฐ๋ผ ๋ค๋ฅด์ง๋ง REST API ๊ด์ต์์๋ ์ ๊ท ์์ฑ์ 201์ด ์ผ๋ฐ์ ์ ๋๋ค.
# โ ๊ณ ์ ์ผ๋ก 200์ ๊ธฐ๋ํด๋ฒ๋ฆฐ๋ค
assert response.status_code == 200
# โ
API ์ฌ์์ ๋ง๊ฒ ํ์ธํ๋ค
# REST API ๊ด์ต: ์์ฑ ์ฑ๊ณต์ 201
assert response.status_code == 201
# ์ฌ์์ด ์ ๋งคํ ๊ฒฝ์ฐ๋ ๋ ๋ค ํ์ฉํ๋ ์์ฑ๋ฒ๋ ๊ฐ๋ฅ
assert response.status_code in [200, 201]๐ก ํฌ์ธํธ๏ผํ ์คํธ๋ฅผ ์์ฑํ๊ธฐ ์ ์ API ๋ฌธ์์์ ๊ธฐ๋ํ๋ ์ํ ์ฝ๋๋ฅผ ํ์ธํ์ธ์.
โข ์๋ต ๋ฐ๋์ ์ ์กํ ๋ฐ์ดํฐ๊ฐ ํฌํจ๋์ง ์๋๋ค
POST ์๋ต์๋ ์ ์กํ ๋ฐ์ดํฐ๊ฐ ๊ทธ๋๋ก ๋ฐํ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ์ง๋ง, ์๋ฒ ์ธก์์ ๊ฐ๊ณต๋ ๋ฐ์ดํฐ๊ฐ ๋ฐํ๋๋ ๊ฒฝ์ฐ๊ฐ ์์์ต๋๋ค.
# โ ์ ์ก ๋ฐ์ดํฐ์ ์์ ์ผ์น๋ฅผ ๊ธฐ๋ํด๋ฒ๋ฆฐ๋ค
assert body == new_user # ์๋ฒ ์ธก์์ id๊ฐ ์ถ๊ฐ๋๋ฏ๋ก ์คํจ
# โ
ํ์ํ ํ๋๋ง ๊ฐ๋ณ๋ก ๊ฒ์ฆํ๋ค
assert body["name"] == new_user["name"]
assert body["email"] == new_user["email"]
assert "id" in body # ์๋ฒ๊ฐ ๋ถ์ฌํ ID๋ ํ์ธ๐ก ํฌ์ธํธ๏ผPOST ์๋ต์๋ ์๋ฒ๊ฐ ๋ถ์ฌํ ID๋ ์๋ ์์ฑ ํ๋๊ฐ ์ถ๊ฐ๋ฉ๋๋ค. ์์ ์ผ์น๊ฐ ์๋ ํ์ํ ํ๋๋ฅผ ๊ฐ๋ณ๋ก ๊ฒ์ฆํ๋ ํธ์ด ๊ฒฌ๊ณ ํฉ๋๋ค.
โฃ ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ ์ํ ์ฝ๋๊ฐ 400์ธ์ง 422์ธ์ง ํท๊ฐ๋ฆฐ๋ค
์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ ์ ์ํ ์ฝ๋๊ฐ 400์ธ์ง 422์ธ์ง ํท๊ฐ๋ฆฌ๋ ๊ฒฝ์ฐ๊ฐ ์์์ต๋๋ค.
# โ 400๋ง์ ๊ธฐ๋ํด๋ฒ๋ฆฐ๋ค
assert response.status_code == 400
# โ
API ์ฌ์์ ๋ฐ๋ผ ๋ ๋ค ํ์ฉํ๋ค
assert response.status_code in [400, 422]
# 400 Bad Request โ ์ผ๋ฐ์ ์ธ ์์ฒญ ์ค๋ฅ
# 422 Unprocessable โ ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ๏ผFastAPI ๋ฑ์์ ๋ง์ด ์ฌ์ฉ๏ผ๐ก ํฌ์ธํธ๏ผ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ ์ํ ์ฝ๋๋ ํ๋ ์์ํฌ์ ๋ฐ๋ผ ๋ค๋ฆ ๋๋ค. Express๋ 400, FastAPI๋ 422๋ฅผ ๋ฐํํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. API ๋ฌธ์๋ก ํ์ธํ์ธ์.
โค ๋ชฉ API์์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๊ฒ์ฆํ ์ ์๋ค
JSONPlaceholder์์ ์ ํจ์ฑ ๊ฒ์ฌ ํ ์คํธ๋ฅผ ์์ฑํ๋๋ ๋ถ์ ํ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ 201์ด ๋ฐํ๋์ด๋ฒ๋ ค ํ ์คํธ์ ์๋ฏธ๊ฐ ์์ด์ก์ต๋๋ค.
# JSONPlaceholder๋ ๋ชฉ์ด๋ฏ๋ก
# ๋ถ์ ํ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ 201์ด ๋ฐํ๋์ด๋ฒ๋ฆฐ๋ค
response = requests.post(url, json={"email": "not-an-email"})
print(response.status_code) # โ 201๏ผ๋ณธ๋๋ 400์ด ๊ธฐ๋๋จ๏ผโ ๏ธ ์ฃผ์๏ผ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ ํํ๊ฒ ํ ์คํธํ๋ ค๋ฉด ์ค์ ์ ํจ์ฑ ๊ฒ์ฌ ๊ธฐ๋ฅ์ ๊ฐ์ง API๊ฐ ํ์ํฉ๋๋ค. Restful Booker๋ reqres.in ๋ฑ์ ์ฌ์ฉํ๊ฑฐ๋ ์ง์ ๋ง๋ API๋ก ๊ฒ์ฆํ์ธ์.
07. ์์ฃผ ๋ฌป๋ ์ง๋ฌธ๏ผFAQ๏ผ
Q. POST ํ
์คํธ์์ ์ต์ํ ํ์ธํด์ผ ํ ๊ฒ์ ๋ฌด์์ธ๊ฐ์๏ผ
A. ์ต์ํ ใ์ํ ์ฝ๋๊ฐ 201์ธ ๊ฒใใ์ ์กํ ๋ฐ์ดํฐ๊ฐ ์๋ต์ ํฌํจ๋์ด ์๋ ๊ฒใใID๊ฐ ๋ถ์ฌ๋์ด ์๋ ๊ฒใ์ 3๊ฐ์ง์
๋๋ค. ์ฌ์ ๊ฐ ์๋ค๋ฉด ์ ํจ์ฑ ๊ฒ์ฌใป์คํค๋ง ๊ฒ์ฆใปํค๋ ํ์ธ๋ ์ถ๊ฐํ๋ฉด ์ค๋ฌด ์์ค์ ํ
์คํธ๊ฐ ๋ฉ๋๋ค.
Q. requests.post()๋ก JSON์ ๋ณด๋ด๋ ๋ฐฉ๋ฒ์๏ผ
A. requests.post(url, json=๋ฐ์ดํฐ) ๋ฅผ ์ฌ์ฉํฉ๋๋ค. json= ์ ์ฌ์ฉํ๋ฉด Content-Type์ด ์๋์ผ๋ก application/json ์ผ๋ก ์ค์ ๋ฉ๋๋ค. data= ๋ฅผ ์ฌ์ฉํ๋ฉด ํผ ๋ฐ์ดํฐ๋ก ์ ์ก๋์ด๋ฒ๋ฆฌ๋ฏ๋ก JSON API์๋ ๋ฐ๋์ json= ์ ์ฌ์ฉํ์ธ์.
Q. GET ํ
์คํธ์ POST ํ
์คํธ์ ์ฐจ์ด๋ ๋ฌด์์ธ๊ฐ์๏ผ
A. GET์ ใ๋ฐ์ดํฐ ์ทจ๋ใ, POST๋ ใ๋ฐ์ดํฐ ์์ฑใ์
๋๋ค. POST ํ
์คํธ๋ ์์ฒญ ๋ฐ๋๋ฅผ ์ ์กํ๋ ์ , ์ฑ๊ณต ์ ์ํ ์ฝ๋๊ฐ 201์ธ ์ , ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ ๊ฒ์ฆ์ด ํ์ํ ์ ์ด GET๊ณผ ๋ค๋ฆ
๋๋ค.
Q. ์ ํจ์ฑ ๊ฒ์ฌ ํ
์คํธ๋ ์ด๋ API๋ก ์ํํด๋ณผ ์ ์๋์๏ผ
A. JSONPlaceholder๋ ๋ชฉ API์ด๋ฏ๋ก ์ ํจ์ฑ ๊ฒ์ฌ ๊ธฐ๋ฅ์ด ์์ต๋๋ค. ๋ณธ๊ฒฉ์ ์ธ ์ ํจ์ฑ ๊ฒ์ฌ ํ
์คํธ์๋ reqres.in ์ด๋ ์ง์ ๋ง๋ API๋ฅผ ์ฌ์ฉํ์ธ์. ํฌํธํด๋ฆฌ์ค์ฉ์ผ๋ก๋ Restful Booker์ ์ธ์ฆ ์๋ํฌ์ธํธ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Q. POST์ PUT์ ์ฐจ์ด๋ ๋ฌด์์ธ๊ฐ์๏ผ
A. POST๋ ใ์ ๊ท ์์ฑใ, PUT์ ใ๊ธฐ์กด ๋ฐ์ดํฐ์ ์ ์ฒด ์
๋ฐ์ดํธใ์
๋๋ค. POST๋ ๋งค๋ฒ ์๋ก์ด ๋ฆฌ์์ค๊ฐ ์์ฑ๋๋ ๋ฐ๋ฉด PUT์ ๊ฐ์ ์์ฒญ์ ๋ช ๋ฒ ๋ณด๋ด๋ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋ฉ๋๋ค๏ผ๋ฉฑ๋ฑ์ฑ๏ผ. ๋ค์ ๊ธ์์ PUTใปPATCH ํ
์คํธ๋ฅผ ํด์คํฉ๋๋ค.
08. ์ ๋ฆฌ
Python์ผ๋ก API POST ํ ์คํธ๋ฅผ ๊ตฌํํ๋ ๊ฒฝ์ฐ, pytest์ requests๋ฅผ ์ฌ์ฉํด์ ์ํ ์ฝ๋ใป์๋ต ๋ฐ๋ใปID ๋ถ์ฌใป์ ํจ์ฑ ๊ฒ์ฌ์ 4๊ฐ์ง๋ฅผ ๊ฒ์ฆํ๋ ๊ฒ์ด ์ค๋ฌด์ ๊ธฐ๋ณธ์ ๋๋ค.
์ด ๊ธ์์๋ pytest์ requests๋ฅผ ์ฌ์ฉํ API POST ํ ์คํธ ๊ตฌํ ๋ฐฉ๋ฒ์ ํด์คํ์ต๋๋ค.
| ํ ์คํธ ์ผ์ด์ค | ๋ด์ฉ |
|---|---|
| TC01 | ์ํ ์ฝ๋๊ฐ 201์ด์ด์ผ ํ๋ค |
| TC02 | raise_for_status()๋ก 4xx/5xx๋ฅผ ๊ฒ์ง |
| TC03 | Content-Type์ด JSON์ด์ด์ผ ํ๋ค |
| TC04 | ์ ์ก ๋ฐ์ดํฐ๊ฐ ์๋ต์ ์ฌ๋ฐ๋ฅด๊ฒ ํฌํจ๋์ด์ผ ํ๋ค |
| TC05 | ์์ฑ๋ ๋ฆฌ์์ค์ ID๊ฐ ๋ถ์ฌ๋์ด์ผ ํ๋ค |
| TC06 | ํ์ ํ๋ ์์ ๋ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค |
| TC07 | ๋น ๋ฌธ์์ด ์ ์ก ์ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค |
| TC08 | ์๋ชป๋ ์ด๋ฉ์ผ ํ์ ์ ์ ์ ํ ์ค๋ฅ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค |
| TC09 | ์๋ต ์คํค๋ง๏ผํ์ ๏ผ๊ฐ ์ฌ๋ฐ๋ฅด๋ค |
๋ค์ ๊ธ์์๋ PUTใปPATCH ์์ฒญ์ API ํ ์คํธ๏ผ๊ธฐ์กด ๋ฐ์ดํฐ์ ์ ๋ฐ์ดํธใป๋ถ๋ถ ์ ๋ฐ์ดํธ๏ผ๋ฅผ ํด์คํฉ๋๋ค.

