关于单元测试:Python:模拟datetime.date.today()

Python: Mocking datetime.date.today()

试着让mocker为datetime.date.today()工作,在阅读了这个答案和本文档后,我终于开始工作了。我没有得到我的原始版本,我想知道是否有人可以帮助我理解?

我原来的实现是在导入日期时间之后调用dt.date.today(),如此import datetime as dt。这可以在mymodule.today_string_dt_date中看到。测试是通过模拟mymodule.dt.date(不可能直接模拟mymodule.dt.date.today,因为它是内置),并在导入datetime之后以与mymodule.py(import datetime as dt)相同的方式设置mock.today.return_value = dt.date(2016,1,10)

然后,我实现了一个直接导入date的版本(from datetime import date),并创建了将mock.today.return_value设置为dt.date(2016,1,10)date(2016,1,10)两个版本的测试。所有测试版本,但我的原始测试版本正在通过,正如在运行py.test -v的输出中可以看到的那样

看起来对模块和测试使用相同的导入方案(import datetime as dt)并使用它来创建模拟返回值(如在测试test_today_string_dt_date中)废弃模拟。

mymodule.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import datetime as dt
from datetime import date


def today_string_dt_date():
    return dt.date.today().strftime('%Y-%m-%d')


def today_string_date():
    return date.today().strftime('%Y-%m-%d')


if __name__ == '__main__':
    print(today_string_dt_date())

test_mymodule.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import datetime as dt
from datetime import date
from unittest.mock import patch
import unittest

import mymodule


class MyTest(unittest.TestCase):
    @patch('mymodule.dt.date')
    def test_today_string_dt_date(self, mock_date):
        mock_date.today.return_value = dt.date(2016, 1, 10)
        assert mymodule.today_string_dt_date() == '2016-01-10'

    @patch('mymodule.dt.date')
    def test_today_string_dt_date_alt(self, mock_date):
        mock_date.today.return_value = date(2016, 1, 10)
        assert mymodule.today_string_dt_date() == '2016-01-10'

    @patch('mymodule.date')
    def test_today_string_date(self, mock_date):
        mock_date.today.return_value = dt.date(2016, 1, 10)
        assert mymodule.today_string_date() == '2016-01-10'

    @patch('mymodule.date')
    def test_today_string_date_alt(self, mock_date):
        mock_date.today.return_value = date(2016, 1, 10)
        assert mymodule.today_string_date() == '2016-01-10'

py.test输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
=========================== test session starts ===========================
platform darwin -- Python 3.5.1, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
collected 4 items

test_mymodule.py::MyTest::test_today_string_date PASSED
test_mymodule.py::MyTest::test_today_string_date_alt PASSED
test_mymodule.py::MyTest::test_today_string_dt_date FAILED
test_mymodule.py::MyTest::test_today_string_dt_date_alt PASSED

================================ FAILURES =================================
____________________ MyTest.test_today_string_dt_date _____________________

self = <test_mymodule.MyTest testMethod=test_today_string_dt_date>
mock_date = <MagicMock name='date' id='4364815888'>

    @patch('mymodule.dt.date')
    def test_today_string_dt_date(self, mock_date):
        mock_date.today.return_value = dt.date(2016, 1, 10)    
>       assert mymodule.today_string_dt_date() == '2016-01-10'
E       AssertionError: assert <MagicMock name='date().strftime()'     id='4353329528'> == '2016-01-10'
E        +  where <MagicMock name='date().strftime()' id='4353329528'> = <function today_string_dt_date at 0x10429ed90>()
E        +    where <function today_string_dt_date at 0x10429ed90> = mymodule.today_string_dt_date

test_mymodule.py:14: AssertionError
=================== 1 failed, 3 passed in 0.05 seconds ====================


这只是因为@patch('mymodule.dt.date')你正在修补datetime模块中的原始date引用。 在此上下文中,dt.date(2016, 1, 10)MagicMock,其中名称是date()

从这里你有today_string_dt_date()将返回date().strftime() MagicMock

我承认这有点扭曲,我尝试避免复杂路径中的这种事情:我的规则是

  • 尝试仅修补根引用。
  • 如果你不能跟随1(带有from a import b的模块),只需修补模块参考,不要从中进行浏览。