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

使用pytest.mark.parametrize时Mock继承类方法的单元测试问题

如何结合pytest参数化与Mock避免API调用并验证URL

问题场景

我是单元测试新手,开发了一套从API获取数据、反序列化为JSON并写入数据库的类,目前这些类均可正常运行,正在编写对应的单元测试。现有类定义如下:

import requests


class GetResponse():

    def getDeserialize(self, url: str):
        
        ApiResponse = requests.get(f'{url}')
        
        toJson = ApiResponse.json()
        
        return toJson


class MercadoBitcoin(GetResponse):

    def __init__(self) -> None:
        self.beginningOfUrl = 'https://www.mercadobitcoin.net/api'


    # This method is to get generally from the API
    def standardGet(self, coin: str, method: str):

        self.URL = f'{self.beginningOfUrl}/{coin}/{method}/'
        
        urlGet = super().getDeserialize(self.URL)

        return urlGet

    
    # This method is to get specifically from the API Day Summary
    def daySummary(self, year: int, month: int, day: int, coin: str):
        
        method = 'day-summary'

        self.URL = f'{self.beginningOfUrl}/{coin}/{method}/{year}/{month}/{day}'

        urlGet = super().getDeserialize(self.URL)

        return urlGet

我编写了测试用例仅需验证生成的URL与预期一致,无需实际调用API,但尝试的两种Mock方式均无效,请求指导正确的实现方式。

错误原因分析

  • 第一种Mock requests.get 的问题:装饰器顺序导致参数传入顺序错误,patch 的 mock 对象应该作为函数的第一个参数传入,而非最后一个。
  • 第二种Mock super.getDeserialize 的问题:super 不是可被 patch 的目标,需要明确指定要 mock 的父类方法路径。

解决方案

方案一:Mock requests.get,避免实际API调用

调整装饰器顺序(@pytest.mark.parametrize 放在 @patch 上方),并修正函数参数顺序,确保 mock 对象先传入:

from unittest.mock import patch
import pytest
from your_module import MercadoBitcoin  # 替换为你的类所在模块名

class TestMercadoBitcoin():
    
    @pytest.mark.parametrize(
        "coin, method, expected",
        [
            ("BTC", "ticker", "https://www.mercadobitcoin.net/api/BTC/ticker/"),
            ("ETH", "ticker", "https://www.mercadobitcoin.net/api/ETH/ticker/")
        ]
    )
    @patch('requests.get')
    def test_standardGet(self, mock_get, coin, method, expected):
        # 配置mock返回值,避免调用json()时报错
        mock_response = mock_get.return_value
        mock_response.json.return_value = {}
        
        actual = MercadoBitcoin()
        actual.standardGet(coin=coin, method=method)
        
        assert actual.URL == expected
        # 可选:验证requests.get是否被正确调用了一次,传入的URL是否正确
        mock_get.assert_called_once_with(expected)

说明:

  • 装饰器顺序:@pytest.mark.parametrize 在上,@patch 在下,确保参数化逻辑先处理,mock 对象作为第一个参数传入函数。
  • 必须配置 mock 返回值:因为 getDeserialize 会调用 response.json(),所以需要给 mock_get.return_value.json 设置返回值,否则会抛出 AttributeError。
  • 可选验证:通过 mock_get.assert_called_once_with(expected) 可以进一步确认API调用的URL是否符合预期。

方案二:Mock父类GetResponse的getDeserialize方法

如果只想阻止父类的方法执行,而不是mock requests,可以直接mock GetResponse.getDeserialize:

from unittest.mock import patch
import pytest
from your_module import MercadoBitcoin, GetResponse  # 替换为你的类所在模块名

class TestMercadoBitcoin():
    
    @pytest.mark.parametrize(
        "coin, method, expected",
        [
            ("BTC", "ticker", "https://www.mercadobitcoin.net/api/BTC/ticker/"),
            ("ETH", "ticker", "https://www.mercadobitcoin.net/api/ETH/ticker/")
        ]
    )
    @patch.object(GetResponse, 'getDeserialize')
    def test_standardGet(self, mock_get_deserialize, coin, method, expected):
        # 配置mock返回值
        mock_get_deserialize.return_value = {}
        
        actual = MercadoBitcoin()
        actual.standardGet(coin=coin, method=method)
        
        assert actual.URL == expected
        # 可选:验证父类方法是否被正确调用
        mock_get_deserialize.assert_called_once_with(expected)

说明:

  • 使用 @patch.object 直接mock父类的方法,避免了对requests的依赖。
  • 同样需要配置mock的返回值,因为 standardGet 会接收父类方法的返回值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 06:54:08