📌 この記事はこんな方におすすめ
- PythonとrequestsでDELETEリクエストのテストを書きたい方
- APIテストでデータ削除の検証方法を学びたいQAエンジニアの方
- CRUD操作のAPIテストを完成させたい方
- 削除後の404確認まで含めた堅牢なDELETEテストを実装したい方
✅ この記事を読むとわかること
- pytestとrequestsでDELETEリクエストのAPIテストを書く基本パターン
- 削除成功時のステータスコード(200・204)の違いと使い分け
- 削除後に404が返ることを確認する実装方法
- raise_for_status()を使った実務レベルのテストコードの書き方
PythonでAPIテストを書くとき、CRUD操作の最後がDELETEリクエストのテストです。pytestとrequestsを使えば、APIの削除処理が正しく動くかを自動テストで検証できます。
この記事では、DELETEテストの基本から削除後の404確認までを実務で使えるコードとともに紹介します。
00. APIテストとDELETEリクエストの基本
DELETEリクエストは「リソースを削除する」操作です。GETやPOSTと比べてシンプルですが、削除後のリソースの状態確認が重要です。
| 確認項目 | 内容 | 例 |
|---|---|---|
| ステータスコード | 削除成功時は200か204が返るか | 200 OK / 204 No Content |
| 削除後の確認 | 削除後にGETすると404が返るか | 404 Not Found |
| 存在しないリソース | 存在しないIDを削除しようとしたとき適切なエラーが返るか | 404 Not Found |
| レスポンスボディ | 204の場合はボディが空か | {} または 空 |
💡 ポイント:DELETEテストは「削除できた」だけでなく「削除後にアクセスできなくなった」まで確認することで、本当の意味でのテストになります。
01. pytest + requestsのAPIテスト環境
まだ環境構築が済んでいない方は先にインストールしてください。
pip install requests pytest pytest-html今回のテスト対象は無料のモックAPI JSONPlaceholder です。
DELETE https://jsonplaceholder.typicode.com/users/1💡 ポイント:JSONPlaceholderはモックAPIのため、DELETEを実行しても実際のデータは削除されません。そのため削除後にGETしても200が返ることがあります。実際のAPIでは削除後のGETで404が返ることを期待してテストを書くケースが多いです。
02. PythonでAPIのDELETEテストを書く方法
基本的なDELETEテスト(200確認)
DELETEリクエストを送り、200が返ることを確認します。
import requests
BASE_URL = "https://jsonplaceholder.typicode.com"
def test_delete_user_status_code():
"""TC01: DELETEで削除時にステータスコード200が返ること"""
response = requests.delete(f"{BASE_URL}/users/1")
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_delete_user_raise_for_status():
"""TC02: 4xx/5xxエラー時に例外が発生すること"""
response = requests.delete(f"{BASE_URL}/users/1")
response.raise_for_status()
assert response.status_code == 200
print(f"\n✅ TC02 PASS | raise_for_status() 正常通過")💡 ポイント:raise_for_status() を使うと4xx/5xxを例外として検知できます。ただしAPIテストでは意図的に4xx/5xxを確認するケースもあるため、status_codeのassertと使い分けることが重要です。
レスポンスボディの確認
DELETEのレスポンスボディは空か、空のJSONが返ることを確認します。
def test_delete_user_response_body():
"""TC03: DELETEのレスポンスボディが空または空JSONであること"""
response = requests.delete(f"{BASE_URL}/users/1")
response.raise_for_status()
# 204 No Contentのときはjson()を呼ぶとJSONDecodeErrorになるため分岐
if response.status_code == 204:
body = None
else:
body = response.json()
assert body in [None, {}], \
f"削除後のレスポンスボディは空が期待値: {body}"
print(f"\n✅ TC03 PASS | レスポンスボディ: {body}")💡 ポイント:DELETEのレスポンスボディはAPIの設計によって異なります。空のJSON `{}`・空文字・204 No Content(ボディなし)のパターンがあります。
03. APIテストのDELETE後の状態確認
削除テストで最も重要なのが「削除後にリソースにアクセスできなくなっているか」の確認です。
削除後にGETすると404が返ることを確認
def test_delete_then_get_returns_404():
"""TC04: 削除後にGETすると404が返ること"""
# Step1: リソースを削除
delete_response = requests.delete(f"{BASE_URL}/users/1")
assert delete_response.status_code == 200, "削除が失敗しました"
# Step2: 削除後にGETしてみる
get_response = requests.get(f"{BASE_URL}/users/1")
# JSONPlaceholderはモックのため200が返ることもある
# 実際のAPIでは404が返ることを検証する
assert get_response.status_code in [200, 404], \
f"削除後のGETは404が期待値: {get_response.status_code}"
print(f"\n✅ TC04 PASS | 削除後のGETステータス: {get_response.status_code}")💡 ポイント:削除後に404が返ることの確認は2ステップのテスト(DELETE→GET)で実現します。これがDELETEテストで最も重要な検証です。
04. APIテストのDELETE異常系パターン
存在しないリソースの削除(404確認)
def test_delete_nonexistent_user():
"""TC05: 存在しないリソースの削除で404が返ること"""
response = requests.delete(f"{BASE_URL}/users/99999")
# JSONPlaceholderはモックのため200が返ることもある
# 実際のAPIでは404が期待される
assert response.status_code in [200, 404], \
f"期待外のステータスコード: {response.status_code}"
print(f"\n✅ TC05 PASS | 存在しないリソースの削除: {response.status_code}")⚠️ 注意:JSONPlaceholderはモックAPIのため、存在しないIDに対しても200を返すことがあります。実際のAPIでは404が返ることを検証してください。
200と204の両方に対応したテスト
def test_delete_user_accepts_200_or_204():
"""TC06: 削除成功時に200または204が返ること"""
response = requests.delete(f"{BASE_URL}/users/1")
# REST APIの慣習:削除成功は200または204
assert response.status_code in [200, 204], \
f"削除成功時は200か204が期待値: {response.status_code}"
print(f"\n✅ TC06 PASS | ステータスコード: {response.status_code}")💡 ポイント:削除成功時のステータスコードはAPIの設計によって200か204が返ります。どちらにも対応できる書き方にしておくと汎用性が上がります。
05. APIテストのDELETEヘッダー検証
def test_delete_user_header():
"""TC07: Content-TypeがJSONであること(204以外の場合)"""
response = requests.delete(f"{BASE_URL}/users/1")
response.raise_for_status()
# 204 No ContentはContent-Typeヘッダーが存在しないことがある
if response.status_code != 204:
content_type = response.headers.get("Content-Type", "")
assert "application/json" in content_type, \
f"Content-Typeが想定外: {content_type}"
print(f"\n✅ TC07 PASS | Content-Type: {content_type}")
else:
print(f"\n✅ TC07 PASS | 204 No Content のためContent-Typeチェックをスキップ")06. DELETEテストのコード全文
"""
DELETE API Test
Target: JSONPlaceholder (https://jsonplaceholder.typicode.com)
Framework: Python + requests + pytest
"""
import requests
BASE_URL = "https://jsonplaceholder.typicode.com"
def test_delete_user_status_code():
"""TC01: DELETEで削除時にステータスコード200が返ること"""
response = requests.delete(f"{BASE_URL}/users/1")
assert response.status_code == 200, \
f"期待値: 200, 実際の値: {response.status_code}"
print(f"\n✅ TC01 PASS | status: {response.status_code}")
def test_delete_user_raise_for_status():
"""TC02: 4xx/5xxエラー時に例外が発生すること"""
response = requests.delete(f"{BASE_URL}/users/1")
response.raise_for_status()
assert response.status_code == 200
print(f"\n✅ TC02 PASS | raise_for_status() 正常通過")
def test_delete_user_response_body():
"""TC03: DELETEのレスポンスボディが空または空JSONであること"""
response = requests.delete(f"{BASE_URL}/users/1")
response.raise_for_status()
if response.status_code == 204:
body = None
else:
body = response.json()
assert body in [None, {}]
print(f"\n✅ TC03 PASS | body: {body}")
def test_delete_then_get_returns_404():
"""TC04: 削除後にGETすると404が返ること"""
delete_response = requests.delete(f"{BASE_URL}/users/1")
assert delete_response.status_code == 200
get_response = requests.get(f"{BASE_URL}/users/1")
assert get_response.status_code in [200, 404]
print(f"\n✅ TC04 PASS | 削除後GETステータス: {get_response.status_code}")
def test_delete_nonexistent_user():
"""TC05: 存在しないリソースの削除で404が返ること"""
response = requests.delete(f"{BASE_URL}/users/99999")
assert response.status_code in [200, 404]
print(f"\n✅ TC05 PASS | status: {response.status_code}")
def test_delete_user_accepts_200_or_204():
"""TC06: 削除成功時に200または204が返ること"""
response = requests.delete(f"{BASE_URL}/users/1")
assert response.status_code in [200, 204]
print(f"\n✅ TC06 PASS | status: {response.status_code}")
def test_delete_user_header():
"""TC07: Content-TypeがJSONであること(204以外の場合)"""
response = requests.delete(f"{BASE_URL}/users/1")
response.raise_for_status()
if response.status_code != 204:
content_type = response.headers.get("Content-Type", "")
assert "application/json" in content_type
print(f"\n✅ TC07 PASS | Content-Type: {content_type}")
else:
print(f"\n✅ TC07 PASS | 204のためスキップ")実行コマンド
pytest test_delete_api.py -v -s実行結果サンプル
test_delete_api.py::test_delete_user_status_code PASSED
test_delete_api.py::test_delete_user_raise_for_status PASSED
test_delete_api.py::test_delete_user_response_body PASSED
test_delete_api.py::test_delete_then_get_returns_404 PASSED
test_delete_api.py::test_delete_nonexistent_user PASSED
test_delete_api.py::test_delete_user_accepts_200_or_204 PASSED
test_delete_api.py::test_delete_user_header PASSED
7 passed in 3.56s ✅07. ハマりポイント
実装中に実際につまずいた箇所をまとめました。同じところでハマる方の参考になれば嬉しいです。
① 削除成功が200か204か迷う
DELETEの成功レスポンスが200なのか204なのかで迷いました。APIの設計によって異なります。
# ❌ 固定で200だけを期待してしまう
assert response.status_code == 200
# ✅ 200と204の両方を許容する
assert response.status_code in [200, 204]
# 200 OK → レスポンスボディあり(削除したリソースの情報など)
# 204 No Content → レスポンスボディなし(シンプルな削除確認)💡 ポイント:テストを書く前にAPIドキュメントで期待するステータスコードを確認しましょう。JSONPlaceholderは200を返します。
② 204のとき response.json() でエラーが出る
204 No Contentのレスポンスで response.json() を呼んだところ、ボディが空のためエラーになりました。
# ❌ 204のときにjson()を呼ぶとエラーになる
body = response.json() # JSONDecodeError!
# ✅ ステータスコードで分岐する
if response.status_code == 204:
print("レスポンスボディなし(204 No Content)")
else:
body = response.json()
print(f"レスポンスボディ: {body}")⚠️ 注意:204 No Contentはレスポンスボディが存在しません。ステータスコードを確認してから json() を呼ぶようにしましょう。
③ モックAPIで削除後の404が確認できない
JSONPlaceholderでDELETE後にGETしても200が返ってきてしまい、削除後の404確認ができませんでした。
# JSONPlaceholderはモックのため
# DELETE後にGETしても200が返ってしまう
requests.delete(f"{BASE_URL}/users/1")
response = requests.get(f"{BASE_URL}/users/1")
print(response.status_code) # → 200(本来は404が期待される)💡 ポイント:削除後の404確認は実際にデータが削除されるAPIで行う必要があります。Restful Bookerや自前のAPIで検証しましょう。
④ DELETEでリクエストボディを送ってしまう
DELETEリクエストにリクエストボディを含めて送ろうとしましたが、DELETEはURLでリソースを指定するため通常ボディは不要です。
# ❌ DELETEにボディを含めるのは多くのREST APIでは一般的でない
response = requests.delete(url, json={"id": 1})
# ✅ DELETEはURLのみで指定するのが基本
response = requests.delete(f"{BASE_URL}/users/1")💡 ポイント:DELETEは通常URLパラメータで削除対象を指定するため、多くのREST APIではリクエストボディは不要です。ただしElasticsearchや一部のAPIでは削除条件や削除理由をボディで送る設計もあります。APIドキュメントで確認しましょう。
⑤ 削除済みリソースを再削除したときの挙動を確認し忘れる
一度削除したリソースを再度削除しようとしたとき、どんなレスポンスが返るかの確認を忘れていました。
# 削除済みリソースを再削除した場合
# 実際のAPIでは 404 Not Found が返ることが多い
response1 = requests.delete(f"{BASE_URL}/users/1") # 1回目:成功(200)
response2 = requests.delete(f"{BASE_URL}/users/1") # 2回目:既に存在しない
assert response1.status_code == 200
# 実際のAPIでは2回目は404が期待される
assert response2.status_code in [200, 404]💡 ポイント:削除済みリソースへの再削除テストは実際のAPIでの検証が必要です。モックAPIでは確認できません。
08. DELETE APIテストのベストプラクティス
DELETE APIテストでは以下の3つを確認することが重要です。
| 確認項目 | 内容 | 重要度 |
|---|---|---|
| ① ステータスコード確認 | 削除成功時は200または204が返ること | ⭐⭐⭐ 必須 |
| ② 削除後の404確認 | 削除後にGETすると404が返ること | ⭐⭐⭐ 重要 |
| ③ 異常系の確認 | 存在しないリソース削除時に適切なエラーが返ること | ⭐⭐ 推奨 |
この3点を確認することでDELETE APIの品質を高いレベルで保証できます。特に削除後にGETすると404が返ることの確認は、単に「削除できた」だけでなく「削除後にアクセスできなくなった」まで検証するため、実務では必ず実施しましょう。
💡 ポイント:GETテスト・POSTテスト・PUT/PATCHテスト・DELETEテストの4種類を実装することで、CRUD全操作をカバーした完全なAPIテストスイートが完成します。これはポートフォリオとしても非常に強力なアピールになります。
09. よくある質問(FAQ)
Q. DELETEテストで最低限確認すべきことは何ですか?
A. 最低限「ステータスコードが200または204であること」の1つです。余裕があれば「削除後にGETすると404が返ること」も追加すると実務レベルのテストになります。
Q. DELETEのステータスコードは200と204どちらが正しいですか?
A. どちらも正しいです。APIの設計によります。削除後にリソースの情報を返す場合は200、何も返さない場合は204が使われることが多いです。テストを書く前にAPIドキュメントで確認してください。
Q. DELETEはリクエストボディが必要ですか?
A. 通常は不要です。DELETEはURLのパスパラメータで削除対象を指定します(例:/users/1)。ただし一部のAPIでは削除理由などをボディに含めることもあります。
Q. 削除後の404確認はどのAPIで試せますか?
A. JSONPlaceholderはモックAPIのため削除後の404確認ができません。reqres.in や自前のAPIを使うか、Restful Bookerの予約削除エンドポイントで試すことができます。
Q. CRUDテストを全部書くと何件になりますか?
A. 今回のシリーズで実装したテストケースはGET(9件)・POST(9件)・PUT/PATCH(9件)・DELETE(7件)の合計34件です。これだけあれば「CRUDが全部できる」というポートフォリオとして十分なアピールになります。
10. まとめ
PythonでAPIのDELETEテストを実装する場合、pytestとrequestsを使ってステータスコードと削除後の状態確認が実務の基本です。DELETEはシンプルなテストですが、削除後に404が返ることまで確認することで完全なCRUDテストになります。
この記事では、pytestとrequestsを使ったAPIのDELETEテスト実装方法を解説しました。
| テストケース | 内容 |
|---|---|
| TC01 | DELETEで削除時にステータスコード200が返ること |
| TC02 | raise_for_status()で4xx/5xxを検知 |
| TC03 | DELETEのレスポンスボディが空または空JSONであること |
| TC04 | 削除後にGETすると404が返ること |
| TC05 | 存在しないリソースの削除で404が返ること |
| TC06 | 削除成功時に200または204が返ること |
| TC07 | Content-TypeがJSONであること |
これでGET・POST・PUT/PATCH・DELETEのCRUD全操作のAPIテストが完成しました。次の記事ではシリーズのまとめとして、実務でのAPIテスト設計の考え方を解説します。

