๐ ์ด ๊ธ์ ์ด๋ฐ ๋ถ๊ป ์ถ์ฒํฉ๋๋ค
- Python๊ณผ requests๋ก PUTใปPATCH ์์ฒญ ํ ์คํธ๋ฅผ ์์ฑํ๊ณ ์ถ์ ๋ถ
- API ํ ์คํธ์์ ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ ๊ฒ์ฆ ๋ฐฉ๋ฒ์ ๋ฐฐ์ฐ๊ณ ์ถ์ QA ์์ง๋์ด
- PUT๊ณผ PATCH์ ์ฐจ์ด๋ฅผ ์ดํดํ๊ณ ํ ์คํธ์ ํ์ฉํ๊ณ ์ถ์ ๋ถ
- pytest๋ก CRUD ์กฐ์์ API ํ ์คํธ๋ฅผ ์์ฑํ๊ณ ์ถ์ ๋ถ
โ ์ด ๊ธ์ ์ฝ์ผ๋ฉด ์ ์ ์๋ ๊ฒ
- PUT๏ผ์ ์ฒด ์ ๋ฐ์ดํธ๏ผ๊ณผ PATCH๏ผ๋ถ๋ถ ์ ๋ฐ์ดํธ๏ผ์ ์ฐจ์ด์ ์ฌ์ฉ ๊ตฌ๋ถ
- pytest์ requests๋ก PUTใปPATCH API ํ ์คํธ๋ฅผ ์์ฑํ๋ ๊ธฐ๋ณธ ํจํด
- ์ ๋ฐ์ดํธ ํ ์๋ต ๊ฒ์ฆ๏ผ์ํใป๋ฐ๋ใปํค๋๏ผ๋ฐฉ๋ฒ
- raise_for_status()๋ฅผ ์ฌ์ฉํ ์ค๋ฌด ์์ค์ ํ ์คํธ ์ฝ๋ ์์ฑ๋ฒ
Python๊ณผ pytest๋ก API ํ ์คํธ๋ฅผ ์์ฑํ ๋ PUT๊ณผ PATCH๋ ๋ ๋ค ใ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐ์ดํธํ๋ใ ์์ฒญ์ ๋๋ค. pytest์ requests๋ฅผ ์ฌ์ฉํด์ ใ์ ์ฒด ์ ๋ฐ์ดํธ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํ๋๊ฐ๏ผใใ๋ถ๋ถ ์ ๋ฐ์ดํธ์์ ์ง์ ํ ํ๋๋ง ๋ฐ๋๋๊ฐ๏ผใ๋ฅผ ์๋์ผ๋ก ๊ฒ์ฆํ๋ ๋ฐฉ๋ฒ์ ํด์คํฉ๋๋ค.
์ด ๊ธ์์๋ PUTใปPATCH์ ์ฐจ์ด๋ถํฐ ์ค๋ฌด์์ ์ฌ์ฉํ ์ ์๋ ํ ์คํธ ์ฝ๋๊น์ง๋ฅผ ์ ๋ฆฌํด์ ์๊ฐํฉ๋๋ค.
- 00. API ํ ์คํธ์์ PUT๊ณผ PATCH์ ์ฐจ์ด
- 01. pytest + requests์ API ํ ์คํธ ํ๊ฒฝ
- 02. Python์ผ๋ก API PUT ํ ์คํธ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ
- 03. Python์ผ๋ก API PATCH ํ ์คํธ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ
- 04. API ํ ์คํธ PUTใปPATCH ์คํค๋ง ๊ฒ์ฆ
- 05. API ํ ์คํธ PUTใปPATCH ์ด์๊ณ ํจํด
- 06. PUTใปPATCH ํ ์คํธ ์ ์ฒด ์ฝ๋
- 07. ์์ฃผ ๊ฒช๋ ๋ฌธ์ & ํด๊ฒฐ๋ฒ
- 08. ์์ฃผ ๋ฌป๋ ์ง๋ฌธ๏ผFAQ๏ผ
- 09. ์ ๋ฆฌ
00. API ํ ์คํธ์์ PUT๊ณผ PATCH์ ์ฐจ์ด
PUT๊ณผ PATCH๋ ๋ ๋ค ใ์ ๋ฐ์ดํธใ์ด์ง๋ง, ์ ๋ฐ์ดํธ ๋ฒ์๊ฐ ๋ค๋ฆ ๋๋ค.
| PUT | PATCH | |
|---|---|---|
| ์ ๋ฐ์ดํธ ๋ฒ์ | ๋ฆฌ์์ค ์ ์ฒด๋ฅผ ๊ต์ฒดํ๋ค | ์ง์ ํ ํ๋๋ง ์ ๋ฐ์ดํธํ๋ค |
| ์๋ตํ ํ๋ | null์ด๋ ๊ธฐ๋ณธ๊ฐ์ด ๋๋ค | ๋ณ๊ฒฝ๋์ง ์๊ณ ์๋ ๊ฐ์ด ์ ์ง๋๋ค |
| ๋ฉฑ๋ฑ์ฑ | ์์๏ผ๋ช ๋ฒ ๋ณด๋ด๋ ๊ฐ์ ๊ฒฐ๊ณผ๏ผ | ๊ตฌํ์ ๋ฐ๋ผ ๋ค๋ฆ |
| ์ฉ๋ | ํ๋กํ ์ ์ฒด ์ ๋ฐ์ดํธ ๋ฑ | ์ด๋ฉ์ผ ์ฃผ์๋ง ๋ณ๊ฒฝ ๋ฑ |
๐ก ํฌ์ธํธ๏ผ์ค๋ฌด์์๋ PATCH๊ฐ ๋ ๋ง์ด ์ฌ์ฉ๋ฉ๋๋ค. ํ๋์ ํ๋๋ง ๋ณ๊ฒฝํ๊ณ ์ถ์ ๋ ์ ์ฒด ํ๋๋ฅผ ์ ์กํ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
01. pytest + requests์ API ํ ์คํธ ํ๊ฒฝ
์์ง ํ๊ฒฝ ๊ตฌ์ถ์ด ์ ๋ ๋ถ์ ๋จผ์ ์ค์นํด์ฃผ์ธ์.
pip install requests pytest pytest-html์ด๋ฒ ํ
์คํธ ๋์์ ๋ฌด๋ฃ ๋ชฉ๏ผmock๏ผ API JSONPlaceholder ์
๋๋ค.
# PUT ์์ฒญ๏ผ์ ์ฒด ์
๋ฐ์ดํธ๏ผ
PUT https://jsonplaceholder.typicode.com/users/1
# PATCH ์์ฒญ๏ผ๋ถ๋ถ ์
๋ฐ์ดํธ๏ผ
PATCH https://jsonplaceholder.typicode.com/users/1๐ก ํฌ์ธํธ๏ผJSONPlaceholder๋ ๋ชฉ API์ด๋ฏ๋ก ์ค์ ๋ก๋ ๋ฐ์ดํฐ๊ฐ ์ ๋ฐ์ดํธ๋์ง ์์ต๋๋ค. ํ์ง๋ง ์๋ต์ ์ค์ API์ ๋์ผํ ํ์์ผ๋ก ๋ฐํ๋๋ฏ๋ก ํ ์คํธ ํ์ต์ ์ต์ ์ ๋๋ค.
02. Python์ผ๋ก API PUT ํ ์คํธ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ
๊ธฐ๋ณธ์ ์ธ PUT ํ ์คํธ๏ผ200 ํ์ธ๏ผ
PUT ์์ฒญ์ผ๋ก ์ ์ฒด ์ ๋ฐ์ดํธ๋ฅผ ํ๊ณ 200์ด ๋ฐํ๋๋ ๊ฒ์ ํ์ธํฉ๋๋ค.
import requests
BASE_URL = "https://jsonplaceholder.typicode.com"
def test_put_user_status_code():
"""TC01: PUT์ผ๋ก ์ ์ฒด ์
๋ฐ์ดํธ ์ ์ํ ์ฝ๋ 200์ด ๋ฐํ๋์ด์ผ ํ๋ค"""
updated_user = {
"id": 1,
"name": "Updated Yoshitsugu",
"username": "yoshitsugu728",
"email": "updated@example.com",
"phone": "090-9999-9999",
"website": "qa-auto-lab.com"
}
response = requests.put(f"{BASE_URL}/users/1", json=updated_user)
assert response.status_code == 200, \
f"๊ธฐ๋๊ฐ: 200, ์ค์ ๊ฐ: {response.status_code}"
print(f"\nโ
TC01 PASS | ์ํ ์ฝ๋: {response.status_code}")raise_for_status()๋ก 4xx/5xx๋ฅผ ํ์คํ ๊ฒ์ง
def test_put_user_raise_for_status():
"""TC02: 4xx/5xx ์ค๋ฅ ์ ์์ธ๊ฐ ๋ฐ์ํด์ผ ํ๋ค"""
updated_user = {"id": 1, "name": "Updated User", "email": "updated@example.com"}
response = requests.put(f"{BASE_URL}/users/1", json=updated_user)
response.raise_for_status()
assert response.status_code == 200
print(f"\nโ
TC02 PASS | raise_for_status() ์ ์ ํต๊ณผ")๐ก ํฌ์ธํธ๏ผresponse.raise_for_status() ๋ ์ค๋ฌด์์ ํ์ 1ํ์
๋๋ค. PUTใปPATCH ํ
์คํธ์์๋ ๋ฐ๋์ ์ถ๊ฐํด๋์ธ์.
PUT ์๋ต ๋ฐ๋ ๊ฒ์ฆ
์ ๋ฐ์ดํธํ ๋ฐ์ดํฐ๊ฐ ์๋ต์ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฐ์๋์ด ์๋์ง ํ์ธํฉ๋๋ค.
def test_put_user_response_body():
"""TC03: PUT์ผ๋ก ์
๋ฐ์ดํธํ ๋ฐ์ดํฐ๊ฐ ์๋ต์ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฐ์๋์ด์ผ ํ๋ค"""
updated_user = {
"id": 1,
"name": "Updated Yoshitsugu",
"email": "updated@example.com"
}
response = requests.put(f"{BASE_URL}/users/1", json=updated_user)
response.raise_for_status()
body = response.json()
assert body["name"] == updated_user["name"], f"name ๋ถ์ผ์น: {body['name']}"
assert body["email"] == updated_user["email"], f"email ๋ถ์ผ์น: {body['email']}"
print(f"\nโ
TC03 PASS | ์
๋ฐ์ดํธ ํ ์ด๋ฆ: {body['name']}")ํค๋ ๊ฒ์ฆ
def test_put_user_header():
"""TC04: Content-Type์ด JSON์ด์ด์ผ ํ๋ค"""
updated_user = {"id": 1, "name": "Test", "email": "test@example.com"}
response = requests.put(f"{BASE_URL}/users/1", json=updated_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โ
TC04 PASS | Content-Type: {content_type}")๐ก ํฌ์ธํธ๏ผContent-Type์ application/json; charset=utf-8 ํํ๋ก ๋ฐํ๋๋ฏ๋ก in ์ผ๋ก ๋ถ๋ถ ์ผ์น ๊ฒ์ฆ์ ์ฌ์ฉํฉ๋๋ค.
03. Python์ผ๋ก API PATCH ํ ์คํธ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ
๊ธฐ๋ณธ์ ์ธ PATCH ํ ์คํธ๏ผ๋ถ๋ถ ์ ๋ฐ์ดํธ ํ์ธ๏ผ
PATCH ์์ฒญ์ผ๋ก ์ผ๋ถ ํ๋๋ง ์ ๋ฐ์ดํธํ๊ณ 200์ด ๋ฐํ๋๋ ๊ฒ์ ํ์ธํฉ๋๋ค.
def test_patch_user_status_code():
"""TC05: PATCH๋ก ๋ถ๋ถ ์
๋ฐ์ดํธ ์ ์ํ ์ฝ๋ 200์ด ๋ฐํ๋์ด์ผ ํ๋ค"""
patch_data = {
"email": "patched@example.com" # email๋ง ์
๋ฐ์ดํธ
}
response = requests.patch(f"{BASE_URL}/users/1", json=patch_data)
assert response.status_code == 200, \
f"๊ธฐ๋๊ฐ: 200, ์ค์ ๊ฐ: {response.status_code}"
print(f"\nโ
TC05 PASS | ์ํ ์ฝ๋: {response.status_code}")PATCH ์๋ต ๋ฐ๋ ๊ฒ์ฆ
์ง์ ํ ํ๋๊ฐ ์ ๋ฐ์ดํธ๋์ด ์๋์ง ํ์ธํฉ๋๋ค.
def test_patch_user_response_body():
"""TC06: PATCH๋ก ์ง์ ํ ํ๋๊ฐ ์
๋ฐ์ดํธ๋์ด์ผ ํ๋ค"""
patch_data = {"email": "patched@example.com"}
response = requests.patch(f"{BASE_URL}/users/1", json=patch_data)
response.raise_for_status()
body = response.json()
assert body["email"] == patch_data["email"], \
f"email์ด ์
๋ฐ์ดํธ๋์ง ์์์ต๋๋ค: {body['email']}"
print(f"\nโ
TC06 PASS | ์
๋ฐ์ดํธ ํ email: {body['email']}")๐ก ํฌ์ธํธ๏ผPATCH๋ ์ง์ ํ ํ๋๋ง ๋ณ๊ฒฝํ๋ ๊ฒ์ด ํน์ง์ ๋๋ค. ๋ค๋ฅธ ํ๋๊ฐ ์๋์น ์๊ฒ ๋ณ๊ฒฝ๋์ง ์์๋์ง๋ ํจ๊ป ํ์ธํ๋ฉด ๋์ฑ ๊ฒฌ๊ณ ํ ํ ์คํธ๊ฐ ๋ฉ๋๋ค.
PATCH๋ก ๋ณต์ ํ๋๋ฅผ ์ ๋ฐ์ดํธ
def test_patch_user_multiple_fields():
"""TC07: PATCH๋ก ๋ณต์ ํ๋๋ฅผ ๋์์ ์
๋ฐ์ดํธํ ์ ์์ด์ผ ํ๋ค"""
patch_data = {
"name": "Patched Name",
"email": "patched@example.com"
}
response = requests.patch(f"{BASE_URL}/users/1", json=patch_data)
response.raise_for_status()
body = response.json()
assert body["name"] == patch_data["name"], f"name ๋ถ์ผ์น: {body['name']}"
assert body["email"] == patch_data["email"], f"email ๋ถ์ผ์น: {body['email']}"
print(f"\nโ
TC07 PASS | ์
๋ฐ์ดํธ ์๋ฃ: {body['name']} / {body['email']}")04. API ํ ์คํธ PUTใปPATCH ์คํค๋ง ๊ฒ์ฆ
์ ๋ฐ์ดํธ๋ ๋ฆฌ์์ค์ ์๋ต์ด ์ฌ๋ฐ๋ฅธ ํ์ ์ธ์ง๋ ํ์ธํฉ๋๋ค.
def test_put_user_schema():
"""TC08: PUT ์๋ต ์คํค๋ง๏ผํ์
๏ผ๊ฐ ์ฌ๋ฐ๋ฅด๋ค"""
updated_user = {"id": 1, "name": "Updated User", "email": "updated@example.com"}
response = requests.put(f"{BASE_URL}/users/1", json=updated_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'])}"
assert isinstance(body["email"], str), f"email์ strํ์ด ๊ธฐ๋๊ฐ: {type(body['email'])}"
print(f"\nโ
TC08 PASS | ์คํค๋ง ๊ฒ์ฆ ์๋ฃ")05. API ํ ์คํธ PUTใปPATCH ์ด์๊ณ ํจํด
์กด์ฌํ์ง ์๋ ๋ฆฌ์์ค์ PUT๏ผ404 ํ์ธ๏ผ
def test_put_nonexistent_user():
"""TC09: ์กด์ฌํ์ง ์๋ ๋ฆฌ์์ค์ PUTํ์ ๋ ์ ์ ํ ์ํ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค"""
updated_user = {"id": 99999, "name": "Ghost User", "email": "ghost@example.com"}
response = requests.put(f"{BASE_URL}/users/99999", json=updated_user)
# JSONPlaceholder๋ ๋ชฉ์ด๋ฏ๋ก 200์ ๋ฐํํ๋ ๊ฒฝ์ฐ๋ ์๋ค
# ์ค์ API์์๋ 404๊ฐ ๊ธฐ๋๋จ
assert response.status_code in [200, 404], \
f"์์ ๋ฐ์ ์ํ ์ฝ๋: {response.status_code}"
print(f"\nโ
TC09 PASS | ์กด์ฌํ์ง ์๋ ๋ฆฌ์์ค์ PUT: {response.status_code}")โ ๏ธ ์ฃผ์๏ผJSONPlaceholder๋ ๋ชฉ API์ด๋ฏ๋ก ์กด์ฌํ์ง ์๋ ID์๋ 200์ ๋ฐํํ๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. ์ค์ API์์๋ 404๊ฐ ๋ฐํ๋๋ ๊ฒ์ ๊ฒ์ฆํด์ฃผ์ธ์.
06. PUTใปPATCH ํ ์คํธ ์ ์ฒด ์ฝ๋
"""
PUT / PATCH API Test
Target: JSONPlaceholder (https://jsonplaceholder.typicode.com)
Framework: Python + requests + pytest
"""
import requests
BASE_URL = "https://jsonplaceholder.typicode.com"
def test_put_user_status_code():
"""TC01: PUT์ผ๋ก ์ ์ฒด ์
๋ฐ์ดํธ ์ ์ํ ์ฝ๋ 200์ด ๋ฐํ๋์ด์ผ ํ๋ค"""
updated_user = {"id": 1, "name": "Updated Yoshitsugu", "email": "updated@example.com"}
response = requests.put(f"{BASE_URL}/users/1", json=updated_user)
assert response.status_code == 200, \
f"๊ธฐ๋๊ฐ: 200, ์ค์ ๊ฐ: {response.status_code}"
print(f"\nโ
TC01 PASS | status: {response.status_code}")
def test_put_user_raise_for_status():
"""TC02: 4xx/5xx ์ค๋ฅ ์ ์์ธ๊ฐ ๋ฐ์ํด์ผ ํ๋ค"""
updated_user = {"id": 1, "name": "Updated User", "email": "updated@example.com"}
response = requests.put(f"{BASE_URL}/users/1", json=updated_user)
response.raise_for_status()
assert response.status_code == 200
print(f"\nโ
TC02 PASS | raise_for_status() ์ ์ ํต๊ณผ")
def test_put_user_response_body():
"""TC03: PUT์ผ๋ก ์
๋ฐ์ดํธํ ๋ฐ์ดํฐ๊ฐ ์๋ต์ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฐ์๋์ด์ผ ํ๋ค"""
updated_user = {"id": 1, "name": "Updated Yoshitsugu", "email": "updated@example.com"}
response = requests.put(f"{BASE_URL}/users/1", json=updated_user)
response.raise_for_status()
body = response.json()
assert body["name"] == updated_user["name"]
assert body["email"] == updated_user["email"]
print(f"\nโ
TC03 PASS | name: {body['name']}")
def test_put_user_header():
"""TC04: Content-Type์ด JSON์ด์ด์ผ ํ๋ค"""
updated_user = {"id": 1, "name": "Test", "email": "test@example.com"}
response = requests.put(f"{BASE_URL}/users/1", json=updated_user)
response.raise_for_status()
content_type = response.headers.get("Content-Type", "")
assert "application/json" in content_type
print(f"\nโ
TC04 PASS | Content-Type: {content_type}")
def test_patch_user_status_code():
"""TC05: PATCH๋ก ๋ถ๋ถ ์
๋ฐ์ดํธ ์ ์ํ ์ฝ๋ 200์ด ๋ฐํ๋์ด์ผ ํ๋ค"""
patch_data = {"email": "patched@example.com"}
response = requests.patch(f"{BASE_URL}/users/1", json=patch_data)
assert response.status_code == 200, \
f"๊ธฐ๋๊ฐ: 200, ์ค์ ๊ฐ: {response.status_code}"
print(f"\nโ
TC05 PASS | status: {response.status_code}")
def test_patch_user_response_body():
"""TC06: PATCH๋ก ์ง์ ํ ํ๋๊ฐ ์
๋ฐ์ดํธ๋์ด์ผ ํ๋ค"""
patch_data = {"email": "patched@example.com"}
response = requests.patch(f"{BASE_URL}/users/1", json=patch_data)
response.raise_for_status()
body = response.json()
assert body["email"] == patch_data["email"]
print(f"\nโ
TC06 PASS | email: {body['email']}")
def test_patch_user_multiple_fields():
"""TC07: PATCH๋ก ๋ณต์ ํ๋๋ฅผ ๋์์ ์
๋ฐ์ดํธํ ์ ์์ด์ผ ํ๋ค"""
patch_data = {"name": "Patched Name", "email": "patched@example.com"}
response = requests.patch(f"{BASE_URL}/users/1", json=patch_data)
response.raise_for_status()
body = response.json()
assert body["name"] == patch_data["name"]
assert body["email"] == patch_data["email"]
print(f"\nโ
TC07 PASS | {body['name']} / {body['email']}")
def test_put_user_schema():
"""TC08: PUT ์๋ต ์คํค๋ง๏ผํ์
๏ผ๊ฐ ์ฌ๋ฐ๋ฅด๋ค"""
updated_user = {"id": 1, "name": "Updated User", "email": "updated@example.com"}
response = requests.put(f"{BASE_URL}/users/1", json=updated_user)
response.raise_for_status()
body = response.json()
assert isinstance(body["id"], int)
assert isinstance(body["name"], str)
assert isinstance(body["email"], str)
print(f"\nโ
TC08 PASS | ์คํค๋ง ๊ฒ์ฆ ์๋ฃ")
def test_put_nonexistent_user():
"""TC09: ์กด์ฌํ์ง ์๋ ๋ฆฌ์์ค์ PUTํ์ ๋ ์ ์ ํ ์ํ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค"""
updated_user = {"id": 99999, "name": "Ghost User", "email": "ghost@example.com"}
response = requests.put(f"{BASE_URL}/users/99999", json=updated_user)
assert response.status_code in [200, 404]
print(f"\nโ
TC09 PASS | status: {response.status_code}")์คํ ์ปค๋งจ๋
pytest test_put_patch_api.py -v -s์คํ ๊ฒฐ๊ณผ ์ํ
test_put_patch_api.py::test_put_user_status_code PASSED
test_put_patch_api.py::test_put_user_raise_for_status PASSED
test_put_patch_api.py::test_put_user_response_body PASSED
test_put_patch_api.py::test_put_user_header PASSED
test_put_patch_api.py::test_patch_user_status_code PASSED
test_put_patch_api.py::test_patch_user_response_body PASSED
test_put_patch_api.py::test_patch_user_multiple_fields PASSED
test_put_patch_api.py::test_put_user_schema PASSED
test_put_patch_api.py::test_put_nonexistent_user PASSED
9 passed in 4.23s โ
07. ์์ฃผ ๊ฒช๋ ๋ฌธ์ & ํด๊ฒฐ๋ฒ
๊ตฌํ ์ค ์ค์ ๋ก ๊ฒช์๋ ๋ฌธ์ ๋ค์ ์ ๋ฆฌํ์ต๋๋ค. ๊ฐ์ ๊ณณ์์ ๋งํ๋ ๋ถ๋ค๊ป ๋์์ด ๋๋ฉด ์ข๊ฒ ์ต๋๋ค.
โ PUT์์ ์๋ตํ ํ๋๊ฐ null์ด ๋๋ค
PUT์์ ์ผ๋ถ ํ๋๋ง ์ ์กํ๋๋ ์ ์กํ์ง ์์ ํ๋๊ฐ null์ด๋ ๋น ๊ฐ์ด ๋์ด๋ฒ๋ ธ์ต๋๋ค. PUT์ ๋ฆฌ์์ค ์ ์ฒด๋ฅผ ๊ต์ฒดํ๋ฏ๋ก ์ ์ฒด ํ๋๋ฅผ ๋ณด๋ด์ง ์์ผ๋ฉด ๋ฐ์ดํฐ๊ฐ ์ฌ๋ผ์ง๋๋ค.
# โ PUT์์ ์ผ๋ถ๋ง ๋ณด๋ด๋ฉด ๋ค๋ฅธ ํ๋๊ฐ ์ฌ๋ผ์ง ๊ฐ๋ฅ์ฑ
response = requests.put(url, json={"name": "Updated"})
# โ phone, website ๋ฑ์ด null์ด ๋์ด๋ฒ๋ฆฐ๋ค๏ผ
# โ
PUT์ ์ ์ฒด ํ๋๋ฅผ ํฌํจํด์ ๋ณด๋ธ๋ค
updated_user = {
"id": 1,
"name": "Updated",
"email": "original@example.com", # ๋ณ๊ฒฝํ์ง ์๋ ํ๋๋ ํฌํจ
"phone": "090-1234-5678",
"website": "qa-auto-lab.com"
}
response = requests.put(url, json=updated_user)๐ก ํฌ์ธํธ๏ผ์ผ๋ถ ํ๋๋ง ๋ณ๊ฒฝํ๊ณ ์ถ์ ๊ฒฝ์ฐ๋ PUT์ด ์๋๋ผ PATCH๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ ๋ต์ ๋๋ค.
โก PATCH์์ ์ ๋ฐ์ดํธ๋์ง ์์ ํ๋ ์ฒดํฌ๋ฅผ ์์ด๋ฒ๋ฆฐ๋ค
PATCH์์ 1๊ฐ ํ๋๋ง ์ ๋ฐ์ดํธํ์ ๋ ๋ค๋ฅธ ํ๋๊ฐ ์๋์น ์๊ฒ ๋ณ๊ฒฝ๋์ง ์์๋์ง ํ์ธํ๋ ๊ฒ์ ์์์ต๋๋ค.
# โ ์
๋ฐ์ดํธํ ํ๋๋ง ํ์ธํ๋ฉด ์ถฉ๋ถํ์ง ์๋ค
patch_data = {"email": "new@example.com"}
response = requests.patch(url, json=patch_data)
body = response.json()
assert body["email"] == "new@example.com" # ์ด๊ฒ๋ง์ผ๋ก๋ ๋ถ์กฑ
# โ
์
๋ฐ์ดํธํ์ง ์์ ํ๋๋ ๋ณํ์ง ์์์์ ํ์ธ
assert body["email"] == "new@example.com" # ์
๋ฐ์ดํธ๋์์ ๊ฒ
assert body["name"] == "Leanne Graham" # ๋ณํ์ง ์์์ ๊ฒ๐ก ํฌ์ธํธ๏ผPATCH ํ ์คํธ์์๋ ใ๋ณ๊ฒฝํ ํ๋๊ฐ ๋ณ๊ฒฝ๋์๋คใ๋ฟ๋ง ์๋๋ผ ใ๋ณ๊ฒฝํ์ง ์์ ํ๋๊ฐ ๋ณ๊ฒฝ๋์ง ์์๋คใ๋ ํ์ธํ๋ฉด ์๋ฒฝํฉ๋๋ค.
โข PUT๊ณผ PATCH ์๋ํฌ์ธํธ๋ฅผ ํผ๋ํ๋ค
PUT๊ณผ PATCH๊ฐ ๊ฐ์ ์๋ํฌ์ธํธ URL์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ ๋ ํผ๋ํด๋ฒ๋ ธ์ต๋๋ค.
# โ ๋ฉ์๋๋ฅผ ์ฐฉ๊ฐํด๋ฒ๋ฆฐ๋ค
response = requests.put(url, json=patch_data) # PATCH๋ฅผ ์ธ ์๊ฐ์ด์๋๋ฐ PUT์ ์ฌ์ฉ
# โ
๋ฉ์๋๋ฅผ ๋ช
ํํ๊ฒ ๊ตฌ๋ถํด์ ์ฌ์ฉํ๋ค
# ์ ์ฒด ์
๋ฐ์ดํธ โ PUT
response = requests.put(f"{BASE_URL}/users/1", json=full_data)
# ๋ถ๋ถ ์
๋ฐ์ดํธ โ PATCH
response = requests.patch(f"{BASE_URL}/users/1", json=partial_data)โ ๏ธ ์ฃผ์๏ผURL์ ๊ฐ์๋ HTTP ๋ฉ์๋๊ฐ ๋ค๋ฆ ๋๋ค. ํจ์๋ช ์ put/patch๋ฅผ ๋ช ๊ธฐํ์ฌ ํผ๋์ ๋ฐฉ์งํ์ธ์.
โฃ ์ ๋ฐ์ดํธ ํ ID๊ฐ ๋ณํ์ง ์์๋์ง ํ์ธ์ ์๋๋ค
PUT์ผ๋ก ์ ๋ฐ์ดํธํ ํ ID๊ฐ ์๋์น ์๊ฒ ๋ณํ์ง ์์๋์ง ํ์ธํ๋ ๊ฒ์ ์์์ต๋๋ค.
# โ
์
๋ฐ์ดํธ ํ์๋ ID๊ฐ ๊ฐ์์ง ํ์ธ
updated_user = {"id": 1, "name": "Updated", "email": "updated@example.com"}
response = requests.put(f"{BASE_URL}/users/1", json=updated_user)
body = response.json()
assert body["id"] == 1, "ID๊ฐ ๋ณํด์๋ ์ ๋๋ค"
assert body["name"] == "Updated", "name์ด ์
๋ฐ์ดํธ๋์์ ๊ฒ"
assert body["email"] == "updated@example.com", "email์ด ์
๋ฐ์ดํธ๋์์ ๊ฒ"๐ก ํฌ์ธํธ๏ผ์ ๋ฐ์ดํธ ์กฐ์์์ ID๊ฐ ๋ณํ์ง ์์์์ ํ์ธํ๋ ๊ฒ์ ์ค๋ฌด์์ ์์ฃผ ๊ฐ๊ณผ๋๋ ํ ์คํธ์ ๋๋ค. ๋ฐ๋์ ์ถ๊ฐํ์ธ์.
โค ๋ชฉ API์์ ๋ฉฑ๋ฑ์ฑ์ ํ์ธํ ์ ์๋ค
PUT์ ํน์ง์ธ ใ๋ช ๋ฒ ๋ณด๋ด๋ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋๋ค๏ผ๋ฉฑ๋ฑ์ฑ๏ผใ๋ฅผ JSONPlaceholder๋ก ํ์ธํ๋ ค๊ณ ํ์ง๋ง, ๋ชฉ์ด๊ธฐ ๋๋ฌธ์ ๋งค๋ฒ ๊ฐ์ ์๋ต์ด ๋ฐํ๋์ด ํ์ธํ ์ ์์์ต๋๋ค.
# ๋ฉฑ๋ฑ์ฑ ํ์ธ๏ผ์ค์ API๋ก ์ํํ๋ ๊ฒฝ์ฐ๏ผ
updated_user = {"id": 1, "name": "Idempotent User", "email": "idem@example.com"}
response1 = requests.put(url, json=updated_user) # 1ํ์งธ
response2 = requests.put(url, json=updated_user) # 2ํ์งธ๏ผ๊ฐ์ ์์ฒญ๏ผ
# ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋ฐํ๋๋ ๊ฒ์ ํ์ธ
assert response1.json() == response2.json(), "PUT์ ๋ฉฑ๋ฑ์ด์ด์ผ ํ๋ค"โ ๏ธ ์ฃผ์๏ผ๋ฉฑ๋ฑ์ฑ ํ์ธ์ ์ค์ ๋ก ๋ฐ์ดํฐ๊ฐ ๋ณํ๋ API๋ก ์ํํด์ผ ํฉ๋๋ค. JSONPlaceholder๋ ๋ฐ์ดํฐ๊ฐ ์ ์ง๋์ง ์์ผ๋ฏ๋ก ํ์ธํ ์ ์์ต๋๋ค.
08. ์์ฃผ ๋ฌป๋ ์ง๋ฌธ๏ผFAQ๏ผ
Q. PUT๊ณผ PATCH ์ด๋ ๊ฒ์ ์ฌ์ฉํ๋ฉด ์ข๋์๏ผ
A. ์ผ๋ถ ํ๋๋ง ๋ณ๊ฒฝํ๊ณ ์ถ์ ๊ฒฝ์ฐ๋ PATCH, ๋ฆฌ์์ค ์ ์ฒด๋ฅผ ๊ต์ฒดํ๊ณ ์ถ์ ๊ฒฝ์ฐ๋ PUT์ ์ฌ์ฉํฉ๋๋ค. ์ค๋ฌด์์๋ PATCH๊ฐ ๋ ๋ง์ด ์ฌ์ฉ๋ฉ๋๋ค. ํ๋์ ํ๋๋ง ๋ณ๊ฒฝํ๊ณ ์ถ์ ๋ ์ ์ฒด ํ๋๋ฅผ ์ ์กํ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
Q. PUT ์ฑ๊ณต ์ ์ํ ์ฝ๋๋ 200๊ณผ 204 ์ด๋ ๊ฒ์ด ์ฌ๋ฐ๋ฅธ๊ฐ์๏ผ
A. API ์ค๊ณ์ ๋ฐ๋ผ ๋ค๋ฆ
๋๋ค. ์
๋ฐ์ดํธ ํ ๋ฆฌ์์ค๋ฅผ ๋ฐํํ๋ ๊ฒฝ์ฐ๋ 200, ๋ฐํํ์ง ์๋ ๊ฒฝ์ฐ๋ 204๋ฅผ ๋ฐํํ๋ API๋ ์์ต๋๋ค. ํ
์คํธ๋ฅผ ์์ฑํ๊ธฐ ์ ์ API ๋ฌธ์๋ก ํ์ธํด์ฃผ์ธ์. JSONPlaceholder๋ 200์ ๋ฐํํฉ๋๋ค.
Q. PATCH ํ ๋ค๋ฅธ ํ๋๊ฐ ๋ณํ์ง ์์๋์ง ํ์ธํ๋ ๋ฐฉ๋ฒ์๏ผ
A. PATCH ์ ์ GET์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ทจ๋ํด๋๊ณ PATCH ํ์ ๋ค์ GETํด์ ๋น๊ตํ๋ ๊ฒ์ด ํ์คํฉ๋๋ค. ์ง์ ํ ํ๋๋ง ๋ณ๊ฒฝ๋๊ณ ๋ค๋ฅธ ๊ฒ์ ์๋ ๊ฐ์ธ ์ฑ๋ก ์์์ ๊ฒ์ฆํ ์ ์์ต๋๋ค.
Q. ๋ฉฑ๋ฑ์ฑ๏ผidempotency๏ผ์ด๋ ๋ฌด์์ธ๊ฐ์๏ผ
A. ๊ฐ์ ์์ฒญ์ ๋ช ๋ฒ ๋ณด๋ด๋ ๊ฒฐ๊ณผ๊ฐ ๋ฐ๋์ง ์๋ ์ฑ์ง์
๋๋ค. PUT์ ๋ฉฑ๋ฑ์ฑ์ ๊ฐ์ง์ง๋ง POST๋ ๋งค๋ฒ ์๋ก์ด ๋ฆฌ์์ค๊ฐ ๋ง๋ค์ด์ง๋ฏ๋ก ๋ฉฑ๋ฑ์ด ์๋๋๋ค. API ํ์ง ๊ฒ์ฆ์์ ์ค์ํ ๊ฐ๋
์
๋๋ค.
Q. PUTใปPATCH ํ
์คํธ๋ GETใปPOST ํ
์คํธ ํ์ ์์ฑํด์ผ ํ๋์๏ผ
A. ์. CRUD ์กฐ์ ์์๋๋ก ใGETโPOSTโPUT/PATCHโDELETEใ์ ์์ผ๋ก ๊ตฌํํ๋ฉด ํ
์คํธ์ ํ๋ฆ์ด ์์ฐ์ค๋ฝ์ต๋๋ค. ์
๋ฐ์ดํธ ํ
์คํธ๋ ๊ธฐ์กด ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ ๊ฒ์ด ์ ์ ๊ฐ ๋๋ฏ๋ก GET๊ณผ POST๊ฐ ๋์ํ๋ ๊ฒ์ ๋จผ์ ํ์ธํ์ธ์.
09. ์ ๋ฆฌ
Python์ผ๋ก API PUTใปPATCH ํ ์คํธ๋ฅผ ๊ตฌํํ๋ ๊ฒฝ์ฐ, pytest์ requests๋ฅผ ์ฌ์ฉํด์ ์ํ ์ฝ๋ใป์๋ต ๋ฐ๋ใป์คํค๋ง๋ฅผ ๊ฒ์ฆํ๋ ๊ฒ์ด ์ค๋ฌด์ ๊ธฐ๋ณธ์ ๋๋ค. PUT๏ผ์ ์ฒด ์ ๋ฐ์ดํธ๏ผ๊ณผ PATCH๏ผ๋ถ๋ถ ์ ๋ฐ์ดํธ๏ผ์ ์ฐจ์ด๋ฅผ ์์ํ ํ ์คํธ ์ค๊ณ๊ฐ ์ค์ํฉ๋๋ค.
์ด ๊ธ์์๋ pytest์ requests๋ฅผ ์ฌ์ฉํ API PUTใปPATCH ํ ์คํธ ๊ตฌํ ๋ฐฉ๋ฒ์ ํด์คํ์ต๋๋ค.
| ํ ์คํธ ์ผ์ด์ค | ๋ด์ฉ |
|---|---|
| TC01 | PUT์ผ๋ก ์ ์ฒด ์ ๋ฐ์ดํธ ์ ์ํ ์ฝ๋ 200์ด ๋ฐํ๋์ด์ผ ํ๋ค |
| TC02 | raise_for_status()๋ก 4xx/5xx๋ฅผ ๊ฒ์ง |
| TC03 | PUT์ผ๋ก ์ ๋ฐ์ดํธํ ๋ฐ์ดํฐ๊ฐ ์๋ต์ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฐ์๋์ด์ผ ํ๋ค |
| TC04 | Content-Type์ด JSON์ด์ด์ผ ํ๋ค |
| TC05 | PATCH๋ก ๋ถ๋ถ ์ ๋ฐ์ดํธ ์ ์ํ ์ฝ๋ 200์ด ๋ฐํ๋์ด์ผ ํ๋ค |
| TC06 | PATCH๋ก ์ง์ ํ ํ๋๊ฐ ์ ๋ฐ์ดํธ๋์ด์ผ ํ๋ค |
| TC07 | PATCH๋ก ๋ณต์ ํ๋๋ฅผ ๋์์ ์ ๋ฐ์ดํธํ ์ ์์ด์ผ ํ๋ค |
| TC08 | ์๋ต ์คํค๋ง๏ผํ์ ๏ผ๊ฐ ์ฌ๋ฐ๋ฅด๋ค |
| TC09 | ์กด์ฌํ์ง ์๋ ๋ฆฌ์์ค์ PUTํ์ ๋ ์ ์ ํ ์ํ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค |
๋ค์ ๊ธ์์๋ DELETE ์์ฒญ์ API ํ ์คํธ๏ผ๋ฐ์ดํฐ ์ญ์ ๊ฒ์ฆ๏ผ๋ฅผ ํด์คํฉ๋๋ค.

