如何模拟你的功能?

时间:2018-04-04 13:22:58

标签: python

我无法弄清楚如何在python中模拟我的函数,我已经尝试寻找一些代码,但似乎没有工作,考虑这个布局:

| project.py
| tests.py

project.py

def foobar():
    return

tests.py

import unittest
from unittest.mock import patch
import os

from project import foobar

def os_urandom_mock():
    return 'mocked'

def foobar_mock():
    return 'mocked'


class TestProject(unittest.TestCase):

    # mocked os.urandom worked well
    @patch('os.urandom', side_effect=os_urandom_mock)
    def test_os_urandom_mocked(self, os_urandom_mocked):
        self.assertEqual(os.urandom(), 'mocked')

    # but this doesn't
    @patch('project.foobar', side_effect=foobar_mock)
    def test_foobar_mocked(self, foobar_mocked):
        self.assertEqual(foobar(), 'mocked')

    # and this also doesn't work
    @patch('project.foobar')
    def test_foobar_mocked_another(self, foobar_mocked):
        foobar_mocked.return_value = 'mocked'
        self.assertEqual(foobar(), 'mocked')

    # and this also doesn't work
    def test_foobar_mocked_last_try(self):
        with patch('project.foobar') as foobar_mocked:
            foobar_mocked.return_value = 'mocked'
            self.assertEqual(foobar(), 'mocked')

unittest.main()

所以,python3 tests.py

======================================================================
FAIL: test_foobar_mocked (__main__.TestProject)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python36\lib\unittest\mock.py", line 1179, in patched
    return func(*args, **keywargs)
  File "tests.py", line 24, in test_foobar_mocked
    self.assertEqual(foobar(), 'mocked')
AssertionError: None != 'mocked'

======================================================================
FAIL: test_foobar_mocked_another (__main__.TestProject)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python36\lib\unittest\mock.py", line 1179, in patched
    return func(*args, **keywargs)
  File "tests.py", line 30, in test_foobar_mocked_another
    self.assertEqual(foobar(), 'mocked')
AssertionError: None != 'mocked'

======================================================================
FAIL: test_foobar_mocked_last_try (__main__.TestProject)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 35, in test_foobar_mocked_last_try
    self.assertEqual(foobar(), 'mocked')
AssertionError: None != 'mocked'

----------------------------------------------------------------------
Ran 4 tests in 0.002s

FAILED (failures=3)

正如您所看到的test_os_urandom_mocked没问题,但我尝试模仿我的foobar功能的所有其他测试都失败了,不知道为什么,任何人都可以解释如果有可能吗?

2 个答案:

答案 0 :(得分:3)

您可以通过模块参考补丁功能来完成这项工作:

import unittest
from unittest.mock import patch
import os
import project

def os_urandom_mock():
    return 'mocked'

def foobar_mock():
    return 'mocked'


class TestProject(unittest.TestCase):

    @patch('os.urandom', side_effect=os_urandom_mock)
    def test_os_urandom_mocked(self, os_urandom_mocked):
        self.assertEqual(os.urandom(), 'mocked')

    @patch('project.foobar', side_effect=foobar_mock)
    def test_foobar_mocked(self, foobar_mocked):
        self.assertEqual(project.foobar(), 'mocked')

    @patch('project.foobar')
    def test_foobar_mocked_another(self, foobar_mocked):
        foobar_mocked.return_value = 'mocked'
        self.assertEqual(project.foobar(), 'mocked')

    def test_foobar_mocked_last_try(self):
        with patch('project.foobar') as foobar_mocked:
            foobar_mocked.return_value = 'mocked'
            self.assertEqual(project.foobar(), 'mocked')

unittest.main()

这里要带走的关键是当你给patch一个字符串时,它会替换该路径上的引用 。这种行为得到了很好的证明here。 (感谢wholevinski获取链接。)

使用现在的代码,测试文件中的foobar引用原始函数,因为您在补丁到位之前导入了它。相比之下,如果您通过模块引用它,您将始终使用该模块的参考。

或者,您可以执行评论中建议的wholevinski,并让您的修补程序替换当前范围中的引用 而不是原始模块中的

import unittest
from unittest.mock import patch
import os
from project import foobar

def os_urandom_mock():
    return 'mocked'

def foobar_mock():
    return 'mocked'


class TestProject(unittest.TestCase):
    @patch(__name__ + '.foobar', side_effect=foobar_mock)
    def test_foobar_mocked(self, foobar_mocked):
        self.assertEqual(foobar(), 'mocked')

    @patch(__name__ + '.foobar')
    def test_foobar_mocked_another(self, foobar_mocked):
        foobar_mocked.return_value = 'mocked'
        self.assertEqual(foobar(), 'mocked')

    def test_foobar_mocked_last_try(self):
        with patch(__name__ + '.foobar') as foobar_mocked:
            foobar_mocked.return_value = 'mocked'
            self.assertEqual(foobar(), 'mocked')

unittest.main()

请注意,在大多数情况下,您将修补被测模块正在导入的内容,而不是直接在测试中调用修补后的函数。在这个的情况下,事情往往更清晰:

<强> asdf.py

def baz():
    return 'baz'

<强> project.py:

from asdf import baz

def foobar():
    return baz()

<强> tests.py:

import unittest
from unittest.mock import patch
import os
from project import foobar

def baz_mock():
    return 'mocked'

class TestProject(unittest.TestCase):
    @patch('project.baz', side_effect=baz_mock)
    def test_foobar_mocked(self, foobar_mocked):
        self.assertEqual(foobar(), 'mocked')

    @patch('project.baz')
    def test_foobar_mocked_another(self, baz_mocked):
        foobar_mocked.return_value = 'mocked'
        self.assertEqual(foobar(), 'mocked')

    def test_foobar_mocked_last_try(self):
        with patch('project.baz') as baz_mocked:
            foobar_mocked.return_value = 'mocked'
            self.assertEqual(foobar(), 'mocked')

unittest.main()

答案 1 :(得分:0)

事实上,我真正想做的是模仿post_save django信号,所以this do the trick