You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 01:55:04