Ontwikkel strategieën voor het mocken van externe API's in unit tests
description
Met deze prompt leer je hoe je externe API-interfaces effectief kunt mocken binnen je unit tests, waardoor je tests sneller en betrouwbaarder worden zonder dat ze afhankelijk zijn van netwerkcondities. Het helpt je ook om onderhoudsstrategieën te ontwikkelen voor mocks bij API-wijzigingen, wat veel handmatig werk bespaart.
prompt
Help me bij het ontwikkelen van effectieve strategieën voor het mocken van externe API's binnen mijn unit tests. Mijn API-specificaties: <voer hier de specificaties of documentatie van de externe API in>. Beschrijf hoe ik betrouw ...
try_prompt
generate_helper
disclaimerOnPageApi
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Natuurlijk! Hier is een overzicht van effectieve strategieën voor het mocken van je REST API in Python, inclusief voorbeelden en onderhoudstips:
**1. Gebruik van Mocking Libraries**
Voor Python kun je bijvoorbeeld `unittest.mock` of gespecialiseerde bibliotheken zoals `responses` of `httpretty` gebruiken.
**2. Voorbeeld met `responses`**
`responses` maakt het makkelijk om HTTP-verzoeken te mocken zonder netwerkverkeer.
*Installatie:*
```bash
pip install responses
```
*Voorbeeldcode:*
```python
import responses
import requests
@responses.activate
def test_get_users():
# Mock voor GET /users
responses.add(
responses.GET,
'https://api.example.com/users',
json=[{'id': 1, 'name': 'Jan'}],
status=200
)
# Uitvoeren van de API-aanroep
resp = requests.get('https://api.example.com/users')
assert resp.status_code == 200
assert resp.json() == [{'id': 1, 'name': 'Jan'}]
```
**3. Mocken van POST en DELETE requests**
Voor POST en DELETE kun je vergelijkbare mocks opzetten:
```python
# Mock voor POST /users
responses.add(
responses.POST,
'https://api.example.com/users',
json={'id': 2, 'name': 'Piet'},
status=201
)
# Mock voor DELETE /users/{id}
responses.add(
responses.DELETE,
'https://api.example.com/users/1',
status=204
)
```
**4. Betrouwbare mocks creëren**
- **Consistentie:** Definieer vaste mock-responses die overeenkomen met API-specificaties.
- **Parametrisatie:** Gebruik functies om mocks te genereren afhankelijk van testcases.
- **Edge-cases:** Voeg mocks toe voor foutmetingen, zoals 404 of 500 statuscodes, om robuuste tests te garanderen.
**5. Onderhoud van mocks bij API-updates**
- **Versiebeheer:** Documenteer de mock-specs en versieer ze samen met je code.
- **Automatisering:** Implementeer tests die controleren of mocks nog overeenkomen met de werkelijke API (bijvoorbeeld via contract testing).
- **Regelmatige review:** Controleer periodiek of mocks nog kloppen met de actuele API-implementatie en pas ze aan indien nodig.
**Samenvatting:**
Gebruik mockingbibliotheken zoals `responses` om je API-aanroepen te simuleren. Definieer consistente en uitgebreide mocks voor alle relevante endpoints en test verschillende scenario’s. Houd mocks up-to-date door ze te documenteren en te integreren in je testproces.
Wil je dat ik een volledige voorbeeldtest voor jouw API-specificaties opstel?
provider: deepseek
model: deepseek-chat
Absoluut! Hier is een uitgebreide gids voor het mocken van externe REST API's in je Python unit tests, inclusief strategieën, voorbeelden en onderhoudstips.
### 1. Strategieën voor het Mocken van Externe API's
**Core Principe:** Vervang de daadwerkelijke netwerkaanroep door een gesimuleerd antwoord (mock) dat het verwachte gedrag van de echte API nabootst.
**Waarom Mocken?**
* **Snelheid:** Geen netwerklatentie.
* **Betrouwbaarheid:** Tests falen niet door netwerk- of API-storingen.
* **Voorspelbaarheid:** Volledige controle over zowel succes- als foutscenario's (bijv. 404, 500 errors).
* **Geen Afhankelijkheid:** Tests kunnen draaien zonder internettoegang of credenties.
**Aanbevolen Bibliotheken voor Python:**
* `unittest.mock` (standaard in Python, krachtig en veelzijdig)
* `requests-mock` (specifiek en erg handig voor de `requests` bibliotheek)
* `pytest-mock` (een wrapper rond `unittest.mock` voor prettig gebruik met pytest)
---
### 2. Implementatievoorbeelden (Python)
Laten we ervan uitgaan dat je een module `api_client.py` hebt die de `requests` bibliotheek gebruikt.
**api_client.py:**
```python
import requests
class UserAPIClient:
def __init__(self, base_url):
self.base_url = base_url
def get_all_users(self):
response = requests.get(f"{self.base_url}/users")
response.raise_for_status()
return response.json()
def create_user(self, user_data):
response = requests.post(f"{self.base_url}/users", json=user_data)
response.raise_for_status()
return response.json()
def delete_user(self, user_id):
response = requests.delete(f"{self.base_url}/users/{user_id}")
response.raise_for_status()
return response.status_code == 204
```
#### Voorbeeld 1: Mocken met `unittest.mock` (meest algemene aanpak)
**test_api_client.py:**
```python
import pytest
from unittest.mock import patch, Mock
from api_client import UserAPIClient
class TestUserAPIClient:
BASE_URL = "http://fake-api.test"
@patch('api_client.requests.get') # Patch de 'requests.get' methode IN de api_client module
def test_get_all_users_success(self, mock_get):
# 1. ARRANGE: Stel de mock in
mock_response = Mock()
expected_users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
mock_response.json.return_value = expected_users
mock_response.raise_for_status.return_value = None # .raise_for_status() doet niets
mock_get.return_value = mock_response # requests.get() retourneert onze mock_response
client = UserAPIClient(self.BASE_URL)
# 2. ACT: Roep de te testen methode aan
users = client.get_all_users()
# 3. ASSERT: Controleer resultaat en of de mock correct is aangeroepen
assert users == expected_users
mock_get.assert_called_once_with(f"{self.BASE_URL}/users")
mock_response.raise_for_status.assert_called_once()
@patch('api_client.requests.post')
def test_create_user(self, mock_post):
mock_response = Mock()
expected_new_user = {"id": 3, "name": "Charlie"}
mock_response.json.return_value = expected_new_user
mock_response.raise_for_status.return_value = None
mock_post.return_value = mock_response
client = UserAPIClient(self.BASE_URL)
user_data = {"name": "Charlie"}
result = client.create_user(user_data)
assert result == expected_new_user
# Controleer of post is aangeroepen met de correcte URL en JSON data
mock_post.assert_called_once_with(f"{self.BASE_URL}/users", json=user_data)
@patch('api_client.requests.delete')
def test_delete_user_success(self, mock_delete):
mock_response = Mock()
mock_response.status_code = 204 # Success, no content
mock_response.raise_for_status.return_value = None
mock_delete.return_value = mock_response
client = UserAPIClient(self.BASE_URL)
user_id = 456
result = client.delete_user(user_id)
assert result is True
mock_delete.assert_called_once_with(f"{self.BASE_URL}/users/{user_id}")
@patch('api_client.requests.get')
def test_get_all_users_failure(self, mock_get):
# Testen van een foutscenario (bijv. 404 Not Found)
mock_response = Mock()
mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError("404 Client Error")
mock_get.return_value = mock_response
client = UserAPIClient(self.BASE_URL)
# Controleer of dezelfde exception wordt opgeworpen
with pytest.raises(requests.exceptions.HTTPError):
client.get_all_users()
```
#### Voorbeeld 2: Mocken met `requests-mock` (meer declaratief)
Eerst installeren: `pip install requests-mock`
**test_api_client_requests_mock.py:**
```python
import pytest
import requests_mock
from api_client import UserAPIClient
class TestUserAPIClientWithRequestsMock:
BASE_URL = "http://fake-api.test"
def test_get_all_users_success(self):
client = UserAPIClient(self.BASE_URL)
expected_users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
# Gebruik de requests_mock context manager
with requests_mock.Mocker() as m:
# Stel de mock direct in op de exacte URL
m.get(f"{self.BASE_URL}/users", json=expected_users, status_code=200)
users = client.get_all_users()
assert users == expected_users
# Controleer of de mock precies één keer is aangeroepen
assert m.call_count == 1
def test_create_user(self):
client = UserAPIClient(self.BASE_URL)
user_data = {"name": "Charlie"}
expected_response = {"id": 3, "name": "Charlie"}
with requests_mock.Mocker() as m:
# Match op POST, URL, en controleer de JSON body
m.post(f"{self.BASE_URL}/users", json=expected_response, status_code=201)
result = client.create_user(user_data)
assert result == expected_response
# Optioneel: controleer de request history voor details
assert m.last_request.json() == user_data
```
---
### 3. Tips voor het Onderhouden van Mocks bij API-updates
Dit is een kritisch onderdeel. Slecht onderhouden mocks leiden tot vals-positieve tests.
1. **Centraliseer Mock Data:**
* Sla mock responses op in aparte JSON-bestanden (bijv., `tests/mock_data/users_get_all.json`) of in dictionaries in een aparte module.
* **Voordeel:** Eén plek om data aan te passen. Je tests lezen `json.loads(mock_data.USERS_GET_ALL)` in plaats van hardgecodeerde dictionaries.
2. **Gebruik een Schema-/Contract-Test:**
* Schrijf **één geïntegreerde test** die niet mocket, maar daadwerkelijk tegen een bekende testomgeving (QA/Staging) van de API aanpraat.
* Deze test valideert of jouw mocks en verwachtingen nog overeenkomen met de werkelijkheid. Draai deze minder frequent (bijv. in een nachtelijke build).
* Gebruik bibliotheken zoals `pydantic` om de response van de echte API te valideren tegen een model.
3. **Versionering en Communicatie:**
* Houd de API-versie bij die je mocket (bijv. in een commentaar of een config-bestand).
* Abonneer je op changelogs of release notes van de API-leverancier. API-updates moeten een directe trigger zijn om je mocks te herzien.
4. **Mock op het Juiste Niveau:**
* Mock de **HTTP-bibliotheek** (zoals `requests`), niet je eigen clientmethode. Dit test ook je integratielaag correct.
5. **Automatiseer waar mogelijk:**
* Overweeg tools zoals **OpenAPI-generator** of **Postman**. Soms kunnen deze tools automatisch mock servers genereren vanuit je OpenAPI-specificatie, wat een bron van waarheid wordt voor zowel ontwikkeling als testing.
6. **Documenteer je Mocks:**
* Noteer bij complexe mocks welke API-versie en welke edge-case ze nabootsen. Dit maakt onderhoud later veel eenvoudiger.
**Conclusie:**
Door `unittest.mock` of `requests-mock` te gebruiken, isoleer je je tests volledig en maak je ze razendsnel. Het onderhoudsprobleem los je op door mockdata te centraliseren en te investeren in een enkele contracttest die je waarschuwt wanneer de externe API verandert.