Python 使用 mock 和 datetime.utcnow() 进行测试

Python test using mock with datetime.utcnow()

我的 utils.py 中有以下功能,基本上计算从 1970 年到当前时间的秒数:

import datetime

def get_utc_timestamp():
    d = datetime.datetime.utcnow()
    epoch = datetime.datetime(1970, 1, 1)
    t = (d - epoch).total_seconds()

    return t

我想 运行 该函数的测试用例,但它取决于时间,所以我寻找解决方案并偶然发现了这个问题 我试图在我的测试中应用它_utils.py:

import unittest
from utils import *
from unittest import mock
import datetime

class TestUtils(unittest.TestCase):

    @mock.patch('utils.datetime.datetime')
    def test_get_utc_timestamp(self, mock_dt):
        mock_dt.utcnow = mock.Mock(return_value = datetime.datetime(2019, 8, 27, 8, 52, 12, 703618))
        result = get_utc_timestamp()
        self.assertEqual(result, 1566895932.703618)

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

我在控制台测试了它的结果:

d = datetime.datetime(2019, 8, 27, 8, 52, 12, 703618)
epoch = datetime.datetime(1970, 1, 1)
t = (d - epoch).total_seconds()

return t

它returned

1566895932.703618

但是当我 运行 测试时我得到了 AssertionError:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/unittest/mock.py", line 1179, in patched
    return func(*args, **keywargs)
  File "/app/tests/test_utils.py", line 15, in test_get_utc_timestamp
    self.assertEqual(result, 1566895932.703618)
AssertionError: <MagicMock name='datetime().__sub__().total_seconds()' id='140475857850040'> != 1566895932.703618

我做错了什么?

任何帮助将不胜感激!

编辑:

感谢 ipaleka 对发生的事情的解释,因为我无法更改 python 内置 - 在 class 中使用模拟,所以我需要自定义 class utcnow () to return 自定义时间在test_utils.py:

class NewDate(datetime.datetime):
    @classmethod
    def utcnow(cls):
        return cls(2019, 8, 27, 8, 52, 12, 703618)

datetime.datetime = NewDate

并将测试函数更改为:

def test_get_utc_timestamp(self):
    result = get_utc_timestamp()
    self.assertEqual(result, 1566895932.703618)

您没有做任何与引用的 SO 答案相关的错误,但该示例仅使用 utcnow 函数,而您同时使用 utcnowdatetime(用于创建 epoch变量)。

当您修补模块时,每次调用子模块、方法或其函数都会创建一个 MagicMock。当您调用 epoch = datetime.datetime(1970, 1, 1) 时会发生这种情况。所以基本上你是在比较 MagicMock 和 float。

您应该同时修补它们或只修补 utcnow

@mock.patch('utils.datetime.datetime.utcnow')
def test_get_utc_timestamp(self, mock_dt):
    mock_dt.return_value = datetime.datetime(2019, 8, 27, 8, 52, 12, 703618)
    result = get_utc_timestamp()
    self.assertEqual(result, 1566895932.703618)