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

使用Pytest测试带@ring.redis装饰器的Person类bar方法失败求助

问题描述

我实现了如下Person类:

class Person:
    def __init__(self, n):
        self.n = n
        self.foo = self.fun()

    @ring.redis(redis.RedisClient, coder="json")
    def fun(self):
        another_thirparty_class()
        # 这里是业务逻辑

    def bar(self):
        a = self.foo["a"]
        print(a + 1)

想给bar()写测试用例,但测试一直失败。我已经做了Mock,但@ring.redis装饰器好像在搞事。测试代码如下:

@pytest.fixture
def mock_redis():
    return redis.StrictRedis(
        host="fake-host",
        port="fake-port",
        db="fake-db",
    )

@patch("path.myfile.redis")
@patch("path.myfile.another_thirparty_class")
def test_bar(mock_another_thirparty_class, mockredis):
    mockredis.return_value = mock_redis()
    p = Person("xyz")
    p.bar()

哪怕Mock了Redis,程序还是硬要连Redis,抛出报错:

redis.exceptions.ConnectionError: Error -2 connecting to fake_host:1234. Name or service not known.
解决方案

方案1:直接MockPerson.fun(最省心)

既然测试的是bar,而bar只依赖fun的返回值,完全没必要管Redis装饰器的逻辑,直接Mockfun让它返回预期结果就行:

@patch("path.myfile.Person.fun")
def test_bar(mock_fun):
    # 模拟fun方法的返回值,对应业务逻辑的输出
    mock_fun.return_value = {"a": 5}
    p = Person("xyz")
    p.bar()  # 此时会打印6,符合预期

这样直接绕开了Redis和第三方类的调用,专注测试bar的核心逻辑——取出a并加1打印。

方案2:Mockring.redis装饰器本身

如果需要测试fun的业务逻辑但不想连真实Redis,可以把ring.redis装饰器Mock成啥也不做,直接返回原方法:

@patch("path.myfile.ring.redis")
@patch("path.myfile.another_thirparty_class")
def test_bar(mock_third, mock_ring_redis):
    # 让ring.redis装饰器不做任何包装,直接返回原函数
    mock_ring_redis.return_value = lambda func: func
    # 按需Mock第三方类的行为
    mock_third.return_value = None
    # 假设fun的业务逻辑返回{"a": 3},根据实际情况调整
    with patch("path.myfile.Person.fun") as mock_fun:
        mock_fun.return_value = {"a": 3}
        p = Person("xyz")
        p.bar()

方案3:调整原代码的初始化逻辑(可选)

原代码在__init__里直接调用self.fun(),导致实例化时立刻触发Redis连接,增加了测试难度。改成懒加载模式会更灵活:

class Person:
    def __init__(self, n):
        self.n = n
        self._foo = None

    @property
    def foo(self):
        if self._foo is None:
            self._foo = self.fun()
        return self._foo

    @ring.redis(redis.RedisClient, coder="json")
    def fun(self):
        another_thirparty_class()
        # 这里是业务逻辑

    def bar(self):
        a = self.foo["a"]
        print(a + 1)

这样fun()只会在第一次访问foo时被调用,测试时能更自由地控制Mock时机。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:31:01