2013-01-15 13 views
17

Ho qualcosa di simile in un file di prova python:Come scherzare usando i percorsi relativi alle patch?

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('') 

Quando ho eseguito questo test, ho un ImportError: No module named monkey. Chiaramente, il percorso che sto applicando alle patch non è corretto. Tuttavia, non sono sicuro di come farlo correttamente senza fare scherzi con sys.path o PYTHONPATH.

Eventuali suggerimenti?

risposta

14

Da quanto ho capito, con finta, è necessità di fornire un tratteggiata nome durante l'applicazione delle patch. Fortunatamente, ogni modulo ha accesso a una variabile a livello di modulo speciale __name__ che contiene il nome del modulo. Usando questo, se si vuole correggere le variabili locali al modulo, si dovrebbe essere in grado di fare qualcosa di simile al seguente:

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()) 

Dando per scontato che hai salvato quel file come quicktest.py, i test di unità danno questo risultato :

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

OK 

E, naturalmente, from a.b import c ti dà una variabile pianura c nel pacchetto, quindi questo stesso meccanismo dovrebbe funzionare.

0

Utilizzare il percorso completo per l'importazione. Ad esempio, se si dispone di questo file system:

  • root/
    • dumy/
      • foo/
        • module.py
    • dummy2/
      • module2.py

È possibile importare module.py da module2.py utilizzando:

from root.dummy.foo import module 
+2

-1 in quanto questo non risponde alla mia domanda. Volevo sapere come usarlo con ** percorsi relativi ** e ** percorsi non assoluti **. – Sardathrion

+0

@Sardathrion Ok, ma tu non dici nulla nella tua domanda ... Dici solo che non vuoi usare sys.path – jvallver

0

Penso che questo deriva dal fatto che non si importa monkey ma invece importa ook. Se importi scimmia da .. allora dovrebbe funzionare. Altrimenti, chiama la patch su ook.

+0

Quando lo faccio, ottengo 'TypeError: Need a target to patch valido . Hai fornito: ook' da Mock. – Sardathrion

+0

Quindi importa 'monkey' invece di importare' ook' da esso in modo che la patch originale funzioni effettivamente. –

+0

Non funzionerà ancora poiché 'monkey' non sembra essere trovato in una delle directory in sys.path. (Ecco perché Sardathrion sta facendo l'importazione relativa, credo.) In effetti, il decoratore di patch non si preoccupa delle tue importazioni poiché importerà il modulo in questione. – balu

1

Non so se questo è il modo migliore (o anche consigliato), ma un modo è quello di usare qualcosa come:

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

ho usato la soluzione di leo-the-maniacale fino mi sono imbattuto in questo utilizzando patch.object - che sembra ancora migliore per me:

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('') 

Vantaggi:

  • necessario che il codice standard che è __name__ + '.object_to_be_patched'
  • Tutte le dipendenze del banco di prova sono chiaramente indicate all'inizio del file come import.

Svantaggi:

  • Non si può fare from ..monkey import ook ma è necessario fare from .. import monkey e l'accesso ook attraverso monkey, vale a dire monkey.ook. Nei casi in cui ho bisogno di scrivere questo spesso aggiungerò ook = monkey.ook alle istruzioni di importazione per comodità.
+0

Probabilmente, ci sono diversi svantaggi, ma ** in generale è un approccio molto utile in molti casi **! Grazie! – maxkoryukov

0

Quando si esegue from ..monkey import ook da un modulo pkg1.pgk2.mymodule ciò che si finisce è con pkg1.pgk2.mymodule.ook.

Questo è ora ook è nello spazio dei nomi del modulo in cui è stato eseguito il from ... import .... E questo è l'obiettivo che devi applicare.

Quindi basta applicare patch 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"): 
    .... 

Come sottolineato da altri, se verrà applicata la correzione dallo stesso modulo in cui avete fatto l'importazione, è possibile utilizzare __name__ per ottenere il nome del pacchetto tratteggiata, ma se stai riparando da un altro modulo che devi sillabare.

Ricorda che qualsiasi cosa tu importi è permeabile all'obiettivo modulethatimports.nameimported.

4

Costruire sulla risposta accettata, credo che questo sia il modo più pulito per raggiungere l'obiettivo desiderato:

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('') 
Problemi correlati