Vraag Patch - Waarom werkt de relatieve patch-doelnaam niet?


Ik heb een klasse uit een module geïmporteerd, maar als ik probeer de naam van de klas te patchen zonder zijn module als een voorvoegsel krijg ik een typefout:

TypeError: Need a valid target to patch. You supplied: 'MyClass'

De volgende code geeft me bijvoorbeeld de bovenstaande fout:

import unittest
from mock import Mock, MagicMock, patch
from notification.models import Channel, addChannelWithName, deleteChannelWithName, listAllChannelNames

class TestChannel(unittest.TestCase):
    @patch("Channel")
    def testAddChannelWithNamePutsChannel(self, *args):
        addChannelWithName("channel1")
        Channel.put.assert_called_with()

Hoewel deze tweede versie van de code mij niet de typefout geeft:

import unittest
from mock import Mock, MagicMock, patch
from notification.models import Channel, addChannelWithName, deleteChannelWithName, listAllChannelNames

class TestChannel(unittest.TestCase):
    @patch("notification.models.Channel")
    def testAddChannelWithNamePutsChannel(self, *args):
        addChannelWithName("channel1")
        Channel.put.assert_called_with()

Waarom is dat? Waarom kan ik op andere plaatsen verwijzen naar kanaal als alleen "Kanaal", maar voor de patch heb ik het voorvoegsel van de module nodig om geen foutmelding te krijgen? Ook heb ik het gevoel dat het geven van het volledige module-voorvoegsel ook niet werkt, want wanneer ik Channel.put.assert_called_with () aanroept, krijg ik de foutmelding dat assert_called_with geen attribuut is van Channel.put. Kan iemand uitleggen wat er aan de hand is? Dank je wel!


12
2018-04-17 12:59


oorsprong


antwoorden:


De patch decorateur vereist dat het doelwit een volledig gestippeld pad is, zoals vermeld in de documentatie:

target moet een tekenreeks zijn in de vorm 'package.module.ClassName'. Het doel wordt geïmporteerd en het opgegeven object wordt vervangen door het nieuwe object, dus het doel moet geïmporteerd kunnen worden vanuit de omgeving van waaruit u patch aanroept. Het doel wordt geïmporteerd wanneer de versierde functie wordt uitgevoerd, niet tijdens decoratietijd.

"Channel" is maar een tekenreeks, en patch heeft niet genoeg informatie om de juiste klas te vinden. Dit is niet hetzelfde als de naam Channel u gebruikt elders, dat bovenaan de module wordt geïmporteerd.

De tweede test mislukt omdat Kanaal wordt geïmporteerd in de testmodule dan patch vervangt Channel in notification.models met een mock-object. Welke patch feitelijk doet, is het object wijzigen in naam Kanaal gebruikt binnen notification.models verwijst naar. De naam Kanaal in de testmodule is al gedefinieerd, dus deze wordt niet beïnvloed. Dit wordt hier eigenlijk beter uitgelegd: http://www.voidspace.org.uk/python/mock/patch.html#id1

Om toegang te krijgen tot de gepatchte versie van uw object, kunt u ofwel rechtstreeks toegang krijgen tot de module:

import unittest 
from mock import patch 
from notification.models import Channel, addChannelWithName  
from notification import models 

class TestChannel1(unittest.TestCase): 
    @patch("notification.models.Channel") 
    def testAddChannelWithNamePutsChannel(self, *args): 
        addChannelWithName("channel1") 
        models.Channel.put.assert_called_with("channel1") 

Of gebruik de gepatchte versie die is doorgegeven als een extra argument voor de ingerichte functie:

class TestChannel2(unittest.TestCase): 
    @patch("notification.models.Channel") 
    def testAddChannelWithNamePutsChannel(self, mock_channel): 
        addChannelWithName("channel1") 
        mock_channel.put.assert_called_with("channel1") 

Als u slechts één methode snel wilt patchen op een object, is het meestal gemakkelijker om het te gebruiken patch.object decorateur:

class TestChannel3(unittest.TestCase):
    @patch.object(Channel, 'put')    
    def testAddChannelWithNamePutsChannel(self, *arg): 
        addChannelWithName("channel1") 
        Channel.put.assert_called_with("channel1") 

20
2018-04-17 21:34