Django中用Pytest测试ModelForm时Mock外部API的方法
用pytest测试Django ModelForm的两种场景实现方案
先基于常见的模型/表单结构给出实现,你可以根据自己的代码调整mock的目标路径:
场景1:Mock Mapbox API,避免实际外部调用
假设你的Place模型里把地址解析逻辑抽成了单独的函数(这样mock更清晰),示例代码如下:
模型与表单示例
# models.py import requests from django.db import models def geocode_address(address): """调用Mapbox API解析地址""" response = requests.get( f"https://api.mapbox.com/geocoding/v5/mapbox.places/{address}.json", params={"access_token": "your-token"} ) data = response.json() return data["features"][0]["center"] if data.get("features") else None class Place(models.Model): address = models.CharField(max_length=255) latitude = models.FloatField(null=True, blank=True) longitude = models.FloatField(null=True, blank=True) def save(self, *args, **kwargs): if self.address and not (self.latitude and self.longitude): coords = geocode_address(self.address) if coords: self.longitude, self.latitude = coords super().save(*args, **kwargs) # forms.py from django import forms from .models import Place class PlaceForm(forms.ModelForm): class Meta: model = Place fields = ["address", "latitude", "longitude"]
测试代码
用pytest-mock的mocker fixture直接mock地址解析函数,跳过真实API调用:
# tests/test_forms.py import pytest from myapp.forms import PlaceForm @pytest.mark.django_db def test_place_form_save_with_mocked_api(mocker): # 模拟地址解析返回指定经纬度 mocker.patch("myapp.models.geocode_address", return_value=[-122.4194, 37.7749]) # 构造表单数据 form_data = {"address": "旧金山, CA"} form = PlaceForm(data=form_data) # 验证表单有效性 assert form.is_valid() # 保存表单(此时不会调用真实API) place = form.save() # 校验结果 assert place.latitude == 37.7749 assert place.longitude == -122.4194 # 确认mock函数被正确调用 mocker.patch("myapp.models.geocode_address").assert_called_once_with("旧金山, CA")
场景2:模拟API报错,验证自定义地址校验逻辑
先在表单里添加API异常的处理逻辑,再mock抛出异常验证错误提示:
调整表单添加错误处理
# forms.py from django import forms from django.core.exceptions import ValidationError import requests from .models import Place, geocode_address class PlaceForm(forms.ModelForm): class Meta: model = Place fields = ["address", "latitude", "longitude"] def clean_address(self): address = self.cleaned_data.get("address") if not address: return address try: coords = geocode_address(address) if not coords: raise ValidationError("无法解析该地址,请检查输入格式") except requests.exceptions.RequestException: # 捕获API离线/报错的情况 raise ValidationError("地址服务暂时不可用,请稍后重试") return address
测试代码
mock地址解析函数抛出异常,验证表单返回自定义错误:
# tests/test_forms.py import pytest import requests from myapp.forms import PlaceForm @pytest.mark.django_db def test_place_form_with_api_error(mocker): # 模拟API抛出请求异常 mocker.patch("myapp.models.geocode_address", side_effect=requests.exceptions.ConnectionError) form_data = {"address": "测试地址"} form = PlaceForm(data=form_data) # 验证表单无效 assert not form.is_valid() # 校验自定义错误提示 assert "地址服务暂时不可用,请稍后重试" in form.errors["address"]
内容的提问来源于stack exchange,提问作者WEBBOgrafico
相关产品推荐
相关产品推荐

