2013-01-15 54 views
17

我有一個python的測試文件是這樣的:如何模擬使用補丁相對路徑?

from mock import patch, 
from ..monkey import ook 
[...] 
@patch('monkey.ook', Mock(return_value=None)) 
def test_run_ook (self, mock_ook): 
    self.assertIsNone(ook()) 
    mock_ook.run.assert_called_once_with('') 

當我運行這個測試,我得到一個ImportError: No module named monkey。顯然,我正在修補的路徑不正確。但是,我不確定如何在不與sys.pathPYTHONPATH混淆的情況下正確設置。

任何指針?

回答

14

從我所收集,用模擬物,你需要提供點打補丁時的名字。幸運的是,每個模塊都可以訪問包含模塊名稱的特殊模塊級變量__name__。用這種方法,如果要修補的局部變量的模塊,你應該能夠做到像下面這樣:

import mock 
import unittest 

ook = lambda: "the ook" 


class OokTest(unittest.TestCase): 

    def test_ook(self): 
     with mock.patch(__name__ + '.ook', return_value=None): 
      self.assertIsNone(ook()) 
     self.assertEquals(ook(), "the ook") 

    # the patch decorator should work the same way, I just tend to use the 
    # context manager out of personal preference 
    @mock.patch(__name__ + '.ook', return_value=None) 
    def test_ook_2(self, mock_ook): 
     self.assertIsNone(ook()) 

假設您保存該文件爲quicktest.py,單元測試給這個結果:

$ python -m unittest quicktest 
.. 
---------------------------------------------------------------------- 
Ran 2 tests in 0.001s 

OK 

和當然,from a.b import c給你一個簡單的變量c在你的包,所以同樣的機制應該工作。

0

使用完整的導入路徑。舉例來說,如果你有這樣的文件系統:

  • 根/
    • dumy/
      • 富/
        • module.py
    • dummy2/
      • module2.py

您可以從module2.py使用導入module.py:

from root.dummy.foo import module 
+2

-1,因爲這並沒有回答我的問題。我想知道如何使用**相對**路徑和**不是絕對路徑**。 – Sardathrion

+0

@Sardathrion好的,但是在你的問題中你不會說這些......你只說你不想使用sys.path – jvallver

0

我想這來自於一個事實,你不進口monkey但而是導入ook。如果你從..導入猴子,那麼它應該工作。否則,只需撥打ook

+0

當我這樣做的時候,我得到了TypeError:需要一個有效的補丁。你提供:來自Mock的ook'。 – Sardathrion

+0

然後,不要導入'猴子'而不是導入'ook',所以你的原始補丁將實際上工作。 –

+0

它仍然不起作用,因爲'monkey'似乎沒有在sys.path中的某個目錄中找到。 (這就是爲什麼Sardathrion正在進行我猜測的相對導入。)實際上,修補程序修飾器並不關心你的導入,因爲它會導入有問題的模塊本身。 – balu

1

不知道這是最好的方式(或者甚至建議),但一個方法是使用類似:

from mock import patch, 
from ..monkey import ook 
[...] 

package_base = __package__.rsplit('.', 1)[0] 

@patch('{0}.monkey.ook'.format(package_base), Mock(return_value=None)) 
def test_run_ook (self, mock_ook): 
    self.assertIsNone(ook()) 
    mock_ook.run.assert_called_once_with('') 
12

我用LEO-的躁狂的解決方案,直到我用patch.object碰到這個人來 - 看起來更好對我說:

from unittest.mock import patch, 
from .. import monkey 
[...] 
@patch.object(monkey, 'ook', Mock(return_value=None)) 
def test_run_ook (self, mock_ook): 
    self.assertIsNone(monkey.ook()) 
    mock_ook.run.assert_called_once_with('') 

優點:

  • 無需樣板代碼是__name__ + '.object_to_be_patched'
  • 測試用例的所有依賴關係在文件開頭都明確聲明爲import語句。

缺點:

  • 你不能這樣做,但from ..monkey import ook需要做from .. import monkey和訪問ook通過monkey,即monkey.ook。在需要經常寫入的情況下,爲了方便起見,我會將ook = monkey.ook添加到導入語句中。
+0

可能有幾個缺點,但**一般來說這是很多情況下非常有用的方法**!謝謝! – maxkoryukov

0

當你從模塊pkg1.pgk2.mymodulefrom ..monkey import ook你最後是pkg1.pgk2.mymodule.ook

這就是現在ook是在您執行from ... import ...的模塊的命名空間中。這就是你需要修補的目標。

所以你只是修補pkg1.pkg2.mymodule.ook

from unittest.mock import patch # mypackage.mymodule.patch 
from ..monkey import ook  # mypacket.mymodule.ook 

with patch("pkg1.pgk2.mymodule.ook"): 
    .... 

正如其他人所指出的,如果你是從那裏,你做的進口同樣的模塊打補丁,那麼你可以使用__name__得到虛線包名,但如果你需要從另一個模塊中修補你需要拼出來的模塊。

只要記住,無論您導入的是從目標modulethatimports.nameimported可修補。

4

大廈接受的答案,我認爲,這是實現預期目標清晰的方式:

from mock import patch 
from .. import monkey 

@patch(monkey.__name__+'.ook', Mock(return_value=None)) 
def test_run_ook (self, mock_ook): 
    self.assertIsNone(monkey.ook()) 
    mock_ook.run.assert_called_once_with('')