使用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
相关产品推荐
相关产品推荐

