pytest:单元测试错误对象没有属性'assert_called_once_with

pytest: unittest error object has no attribute 'assert_called_once_with

我在 pytest 中编写单元测试并在 assert_called_once_with 上出错。 我厌倦了使用与 pytest 文档中所示相同的方式,但似乎我遗漏了一些东西。

# Class which I am trying to mock. (./src/Trading.py)
class BaseTrade:
    def __init__(self, name):
        self.name = name

class Trade(BaseTrade):
    def __init__ (self, name):
        BaseTrade.__init__(self, name)

    def get_balance(self, value):
        # do calculation and return some value
        # for demo purpose hard-coding it
        return  value * 10


#unit test (./unitest/test_test.py
import mock
import unittest
import sys

sys.path.append("../src")

import Trading

class TestTradeClass(unittest.TestCase):
    @classmethod
    def setUpClass(self):
        self.expected_balance = 100

    @classmethod
    def tearDownClass(self):
        pass
    def test_trade(self):
        with mock.patch.object(Trading.Trade, 'get_balance', new = lambda self, x: (x * 10) ) as mock_method:
            obj = Trading.Trade("AAPL")
            value = obj.get_balance(10)
            assert value == 100
        mock_method.assert_called_once_with(100)
Error on mock_method.assert_called_once_with(100)
  AttributeError: 'function' object has no attribute 'assert_called_once_with'

很难分辨,但如果 Trading.Trade.get_method() 实际上是一个原始函数而不是方法 - 您可能需要 unittest.mock.create_autospec()

这些有帮助吗?

不清楚 Trading.Trade 是什么。

如果碰巧 Trading 是一个 class,里面有一个 self.trade = Trade(...),那么您的问题就会大不相同。然后,您需要更深入地修补 Trade.get_method。您可能希望从 class Trading 使用它的同一模块导入 Trade(例如 from .trading import Trading, Trade)- not 来自 Trade声明自 - 然后 修补 Trade.get_method.

我现在相信你想要side_effect。这怎么样?一个文件,假设 test.py:

#!/usr/bin/env python
import unittest

import mock


class BaseTrade:
    def __init__(self, name):
        self.name = name


class Trade(BaseTrade):
    def __init__(self, name):
        BaseTrade.__init__(self, name)

    def get_balance(self, value):
        # do calculation and return some value
        # for demo purpose hard-coding it
        return value * 10


class TestTradeClass(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.expected_balance = 100

    def test_trade(self):
        # Without mock
        obj = Trade("AAPL")
        value = obj.get_balance(10)
        assert value == 100

        # With Trade.get_balance param patched
        with mock.patch.object(
            Trade, 'get_balance', side_effect=lambda value: value * 11
        ) as mock_method:
            obj = Trade("AAPL")
            value = obj.get_balance(10)
            assert value == 110
        mock_method.assert_called_once_with(10)


if __name__ == "__main__":
    unittest.main()

chmod +x test.py

./test.py

输出:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

解释:

  • 使用side_effect代替new
  • 合并为一个文件更方便
  • 删除Trading.Trade
  • @classmethod 使用 cls 而不是 self.
  • mock_method.assert_called_once_with(10),因为 side_effect 关心通过 obj.get_balance(10) 传递的值并存在以改变输出。

近一点?如果不是,您能否说明您要模拟的内容?