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

使用Pytest模拟Bing API方法响应失败问题排查

问题:测试geocode_postcode函数时模拟Bing API返回MagicMock而非预期值

问题背景

测试geocode_postcode函数时,为避免调用真实Bing API,使用unittest.mock.patch和自定义StubResponse类模拟响应,但测试返回的是MagicMock对象而非预期的(0,0),导致断言失败。

待测试函数

def geocode_postcode(postcode: str) -> tuple:
    """
    Turn the postcode into a longitude and latitude point

    Inputs
        postocde: postocde for the UK

    Outputs
        point: longitude and latitude as a tuple (longitude, latitude) else None
    """
    try:

        if not postcode or not isinstance(postcode, str):
            raise ValueError("Invalid postcode format")
        
        with Bing(api_key=CONFIG.BING_API_KEY) as geolocator:
            point_location = geolocator.geocode(
                query={"postalCode":postcode,
                        "countryRegion":"UK"},
                exactly_one=True
            )

        if point_location:
            point = (point_location.longitude, point_location.latitude)
        else:
            point = None

        return point
    
    except (GeocoderTimedOut, GeocoderServiceError) as e:
        print(f"Error geocoding postcode {postcode}: {e}")
        return None
    except ValueError as e:
        print(e)
        return None

测试代码

from petrol_locations.helpers import geocode_postcode
from unittest.mock import MagicMock, patch
import pytest

class StubResponse():
    def __init__(self, longitude, latitude):
        self.longitude = longitude
        self.latitude = latitude

@patch("petrol_locations.helpers.Bing")
def test_geocode_postcode_success(mock_geocode):
    mock_response = StubResponse(0,0)
    mock_geocode.return_value.geocode.return_value = mock_response
    response = geocode_postcode("SW1A 2AA")
    print(response)
    assert response == (0, 0)

错误信息

========================================================================================================= FAILURES ==========================================================================================================
_______________________________________________________________________________________________ test_geocode_postcode_success _______________________________________________________________________________________________

mock_geocode = <MagicMock name='Bing' id='139699539770480'>

    @patch("petrol_locations.helpers.Bing")
    def test_geocode_postcode_success(mock_geocode):
        mock_response = StubResponse(0,0)
        mock_geocode.return_value.geocode.return_value = mock_response
        response = geocode_postcode("SW1A 2AA")
        print(response)
>       assert response == (0, 0)
E       AssertionError: assert (<MagicMock n...99539985984'>) == (0, 0)
E         At index 0 diff: <MagicMock name='Bing().__enter__().geocode().longitude' id='139699539973984'> != 0
E         Use -v to get more diff

tests/test_helpers.py:34: AssertionError

返回结果

(<MagicMock name='Bing().__enter__().geocode().longitude' id='139699539973984'>, <MagicMock name='Bing().__enter__().geocode().latitude' id='139699539985984'>)

解决方案

问题核心是Bing类作为上下文管理器使用,你只mock了Bing.return_value.geocode,但忽略了上下文管理器的__enter__方法。with Bing(...) as geolocator中,geolocator是Bing().__enter__()返回的对象,而非直接的Bing()实例,所以需要给__enter__的返回值设置mock。

修改后的测试代码:

@patch("petrol_locations.helpers.Bing")
def test_geocode_postcode_success(mock_geocode):
    mock_response = StubResponse(0,0)
    # 创建上下文管理器返回的mock对象
    mock_context = MagicMock()
    mock_context.geocode.return_value = mock_response
    # 让Bing实例的__enter__方法返回这个mock对象
    mock_geocode.return_value.__enter__.return_value = mock_context
    
    response = geocode_postcode("SW1A 2AA")
    assert response == (0, 0)

说明

  • mock_geocode.return_value对应Bing()实例
  • __enter__.return_value指定了with语句中geolocator变量的实际指向
  • 给这个上下文对象的geocode方法设置返回值为StubResponse实例,这样point_location就是自定义的响应对象,其longitude和latitude就是预期的0和0,而非MagicMock对象。

内容的提问来源于stack exchange,提问作者Philip09

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 02:27:44