unittest.mock
— mock-objektbibliotek¶
Tillagd i version 3.3.
Källkod: Lib/unittest/mock.py
unittest.mock
är ett bibliotek för testning i Python. Det låter dig ersätta delar av ditt system under test med mock-objekt och göra påståenden om hur de har använts.
unittest.mock
tillhandahåller en grundläggande Mock
-klass som tar bort behovet av att skapa en mängd stubbar i hela din testsvit. När du har utfört en åtgärd kan du göra påståenden om vilka metoder/attribut som användes och vilka argument de anropades med. Du kan också ange returvärden och ställa in nödvändiga attribut på vanligt sätt.
Dessutom tillhandahåller mock en patch()
-dekorator som hanterar patchning av attribut på modul- och klassnivå inom ramen för ett test, tillsammans med sentinel
för att skapa unika objekt. Se quick guide för några exempel på hur man använder Mock
, MagicMock
och patch()
.
Mock är utformat för att användas med unittest
och baseras på mönstret ”action -> assertion” istället för ”record -> replay” som används av många mocking-ramverk.
Det finns en bakåtport av unittest.mock
för tidigare versioner av Python, tillgänglig som mock på PyPI.
Snabbguide¶
objekten Mock
och MagicMock
skapar alla attribut och metoder när du använder dem och lagrar information om hur de har använts. Du kan konfigurera dem för att ange returvärden eller begränsa vilka attribut som är tillgängliga, och sedan göra påståenden om hur de har använts:
>>> from unittest.mock import MagicMock
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')
side_effect
gör att du kan utföra sidoeffekter, inklusive att skapa ett undantag när en mock anropas:
>>> from unittest.mock import Mock
>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
... return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)
Mock har många andra sätt som du kan konfigurera och styra dess beteende på. Till exempel konfigurerar argumentet spec mock så att den hämtar sin specifikation från ett annat objekt. Försök att komma åt attribut eller metoder på mocken som inte finns i specifikationen kommer att misslyckas med ett AttributeError
.
Dekoratorn/kontexthanteraren patch()
gör det enkelt att mocka klasser eller objekt i en modul som testas. Det objekt du anger kommer att ersättas med en mock (eller annat objekt) under testet och återställas när testet avslutas:
>>> from unittest.mock import patch
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... def test(MockClass1, MockClass2):
... module.ClassName1()
... module.ClassName2()
... assert MockClass1 is module.ClassName1
... assert MockClass2 is module.ClassName2
... assert MockClass1.called
... assert MockClass2.called
...
>>> test()
Anteckning
När du nestar patch-dekoratorer skickas mockarna in till den dekorerade funktionen i samma ordning som de tillämpas (den normala Python-ordningen som dekoratorer tillämpas). Detta betyder nerifrån och upp, så i exemplet ovan skickas mocken för module.ClassName1
in först.
Med patch()
är det viktigt att du patchar objekt i den namnrymd där de slås upp. Detta är normalt enkelt, men för en snabb guide läs where to patch.
Liksom en dekorator kan patch()
användas som en kontexthanterare i en with-sats:
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
... thing = ProductionClass()
... thing.method(1, 2, 3)
...
>>> mock_method.assert_called_once_with(1, 2, 3)
Det finns också patch.dict()
för att ställa in värden i en ordbok bara under ett scope och återställa ordboken till dess ursprungliga tillstånd när testet avslutas:
>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original
Mock stöder mockning av Python magiska metoder. Det enklaste sättet att använda magiska metoder är med MagicMock
-klassen. Det gör att du kan göra saker som:
>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()
Med Mock kan du tilldela funktioner (eller andra Mock-instanser) till magiska metoder och de kommer att anropas på lämpligt sätt. Klassen MagicMock
är bara en Mock-variant som har alla de magiska metoderna förskapade åt dig (i alla fall alla användbara).
Nedan följer ett exempel på hur man använder magiska metoder med den vanliga Mock-klassen:
>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='wheeeeee')
>>> str(mock)
'wheeeeee'
För att säkerställa att mock-objekten i dina tester har samma api som de objekt de ersätter kan du använda auto-speccing. Auto-speccing kan göras genom autospec-argumentet till patch, eller funktionen create_autospec()
. Auto-speccing skapar låtsasobjekt som har samma attribut och metoder som de objekt de ersätter, och alla funktioner och metoder (inklusive konstruktörer) har samma anropssignatur som det riktiga objektet.
Detta säkerställer att dina mocks kommer att misslyckas på samma sätt som din produktionskod om de används felaktigt:
>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
... pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
...
TypeError: missing a required argument: 'b'
create_autospec()
kan också användas på klasser, där den kopierar signaturen för metoden __init__
, och på anropsbara objekt där den kopierar signaturen för metoden __call__
.
Den låtsasklassen¶
Mock
är ett flexibelt mock-objekt som är avsett att ersätta användningen av stubbar och testdubblar i hela din kod. Mockar är anropsbara och skapar attribut som nya mockar när du använder dem [1]. Att komma åt samma attribut kommer alltid att returnera samma mock. Mocks registrerar hur du använder dem, så att du kan göra påståenden om vad din kod har gjort med dem.
MagicMock
är en subklass av Mock
med alla magiska metoder förskapade och redo att användas. Det finns också icke anropsbara varianter, användbara när du mockar ut objekt som inte är anropsbara: NonCallableMock
och NonCallableMagicMock
Dekoratorn patch()
gör det enkelt att tillfälligt ersätta klasser i en viss modul med ett Mock
-objekt. Som standard kommer patch()
att skapa en MagicMock
åt dig. Du kan ange en alternativ klass till Mock
med hjälp av argumentet new_callable till patch()
.
- class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶
Skapa ett nytt
Mock
-objekt.Mock
tar flera valfria argument som anger beteendet hos Mock-objektet:spec: Detta kan vara antingen en lista med strängar eller ett befintligt objekt (en klass eller instans) som fungerar som specifikation för mock-objektet. Om du skickar in ett objekt bildas en lista med strängar genom att anropa dir på objektet (exklusive magiska attribut och metoder som inte stöds). Åtkomst till ett attribut som inte finns i listan kommer att ge upphov till ett
AttributeError
.Om spec är ett objekt (snarare än en lista med strängar) returnerar
__class__
klassen för spec-objektet. Detta gör att låtsasobjekt kan klaraisinstance()
-tester.spec_set: En striktare variant av spec. Om den används kommer försök att set eller hämta ett attribut på mock som inte finns på objektet som skickas som spec_set att ge upphov till ett
AttributeError
.side_effect: En funktion som ska anropas när Mock anropas. Se attributet
side_effect
. Användbar för att skapa undantag eller dynamiskt ändra returvärden. Funktionen anropas med samma argument som mock, och om den inte returnerarDEFAULT
, används returvärdet för denna funktion som returvärde.Alternativt kan side_effect vara en undantagsklass eller -instans. I detta fall kommer undantaget att uppstå när mocken anropas.
Om side_effect är en iterabel kommer varje anrop till mock att returnera nästa värde från iterabeln.
En side_effect kan raderas genom att den sätts till
None
.return_value: Det värde som returneras när mocken anropas. Som standard är detta en ny Mock (skapas vid första åtkomst). Se attributet
return_value
.osäker: Som standard kommer åtkomst till alla attribut vars namn börjar med assert, assret, asert, aseert eller assrt att ge upphov till ett
AttributeError
. Om du angerunsafe=True
tillåts åtkomst till dessa attribut.Tillagd i version 3.5.
omsluter: Objekt för det låtsasobjekt som ska omslutas. Om wraps inte är
None
kommer anropet till Mock att skicka anropet vidare till det omslutna objektet (och returnera det verkliga resultatet). Attributåtkomst på mocken kommer att returnera ett Mock-objekt som omsluter motsvarande attribut för det omslutna objektet (så försök att komma åt ett attribut som inte finns kommer att ge upphov till ettAttributeError
).Om mock har ett explicit return_value inställt skickas inte anrop till det inkapslade objektet och return_value returneras istället.
namn: Om mocken har ett namn kommer det att användas i mockens repr. Detta kan vara användbart för felsökning. Namnet sprids till underordnade mockar.
Mocks kan också anropas med godtyckliga nyckelordsargument. Dessa kommer att användas för att ställa in attribut på mocken efter att den har skapats. Se metoden
configure_mock()
för mer information.- assert_called()¶
Försäkra dig om att mocken har anropats minst en gång.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called()
Tillagd i version 3.6.
- assert_called_once()¶
Försäkra dig om att mock anropades exakt en gång.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() Traceback (most recent call last): ... AssertionError: Expected 'method' to have been called once. Called 2 times. Calls: [call(), call()].
Tillagd i version 3.6.
- assert_called_with(*args, **kwargs)¶
Denna metod är ett bekvämt sätt att försäkra sig om att det sista anropet har gjorts på ett visst sätt:
>>> mock = Mock() >>> mock.method(1, 2, 3, test='wow') <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_with(1, 2, 3, test='wow')
- assert_called_once_with(*args, **kwargs)¶
Försäkra dig om att mock anropades exakt en gång och att anropet gjordes med de angivna argumenten.
>>> mock = Mock(return_value=None) >>> mock('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') >>> mock('other', bar='values') >>> mock.assert_called_once_with('other', bar='values') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times. Calls: [call('foo', bar='baz'), call('other', bar='values')].
- assert_any_call(*args, **kwargs)¶
bekräftar att mock har anropats med de angivna argumenten.
Assertet godkänns om mocken alltid har anropats, till skillnad från
assert_called_with()
ochassert_called_once_with()
som bara godkänns om anropet är det senaste, och i fallet medassert_called_once_with()
måste det också vara det enda anropet.>>> mock = Mock(return_value=None) >>> mock(1, 2, arg='thing') >>> mock('some', 'thing', 'else') >>> mock.assert_any_call(1, 2, arg='thing')
- assert_has_calls(calls, any_order=False)¶
försäkrar att mocken har anropats med de angivna anropen. Listan
mock_calls
kontrolleras för anropen.Om any_order är false måste anropen vara sekventiella. Det kan finnas extra anrop före eller efter de angivna anropen.
Om any_order är true kan anropen vara i vilken ordning som helst, men de måste alla visas i
mock_calls
.>>> mock = Mock(return_value=None) >>> mock(1) >>> mock(2) >>> mock(3) >>> mock(4) >>> calls = [call(2), call(3)] >>> mock.assert_has_calls(calls) >>> calls = [call(4), call(2), call(3)] >>> mock.assert_has_calls(calls, any_order=True)
- assert_not_called()¶
Påstå att mocken aldrig kallades.
>>> m = Mock() >>> m.hello.assert_not_called() >>> obj = m.hello() >>> m.hello.assert_not_called() Traceback (most recent call last): ... AssertionError: Expected 'hello' to not have been called. Called 1 times. Calls: [call()].
Tillagd i version 3.5.
- reset_mock(*, return_value=False, side_effect=False)¶
Metoden reset_mock återställer alla anropsattribut på ett mock-objekt:
>>> mock = Mock(return_value=None) >>> mock('hello') >>> mock.called True >>> mock.reset_mock() >>> mock.called False
Detta kan vara användbart om du vill göra en serie påståenden som återanvänder samma objekt.
return_value parameter när den sätts till
True
återställerreturn_value
:>>> mock = Mock(return_value=5) >>> mock('hello') 5 >>> mock.reset_mock(return_value=True) >>> mock('hello') <Mock name='mock()' id='...'>
side_effect parameter när den sätts till
True
återställerside_effect
:>>> mock = Mock(side_effect=ValueError) >>> mock('hello') Traceback (most recent call last): ... ValueError >>> mock.reset_mock(side_effect=True) >>> mock('hello') <Mock name='mock()' id='...'>
Observera att
reset_mock()
inte rensarreturn_value
,side_effect
eller några underordnade attribut som du har ställt in med normal tilldelning som standard.Child mocks återställs också.
Ändrad i version 3.6: Lagt till två argument som endast är nyckelord till reset_mock-funktionen.
- mock_add_spec(spec, spec_set=False)¶
Lägg till en specifikation till en mock. spec kan antingen vara ett objekt eller en lista med strängar. Endast attribut på spec kan hämtas som attribut från mock.
Om spec_set är true kan endast attribut i specifikationen ställas in.
- attach_mock(mock, attribute)¶
Bifogar en mock som ett attribut till den här, och ersätter dess namn och förälder. Anrop till den bifogade mocken kommer att registreras i attributen
method_calls
ochmock_calls
för denna.
- configure_mock(**kwargs)¶
Ställ in attribut på mock genom nyckelordsargument.
Attribut, returvärden och bieffekter kan ställas in på underordnade mocks med hjälp av standardpunktnotation och uppackning av en ordbok i metodanropet:
>>> mock = Mock() >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock.configure_mock(**attrs) >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
Samma sak kan uppnås i konstruktörsanropet till mocks:
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock = Mock(some_attribute='eggs', **attrs) >>> mock.some_attribute 'eggs' >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
configure_mock()
finns för att göra det enklare att konfigurera efter att mocken har skapats.
- __dir__()¶
Mock
-objekt begränsar resultaten avdir(some_mock)
till användbara resultat. För låtsasobjekt med en spec inkluderar detta alla tillåtna attribut för låtsasobjektet.Se
FILTER_DIR
för vad denna filtrering gör och hur du stänger av den.
- _get_child_mock(**kw)¶
Skapa de underordnade mockarna för attribut och returvärde. Som standard kommer underordnade mocks att vara av samma typ som den överordnade. Subklasser av Mock kan vilja åsidosätta detta för att anpassa hur underordnade mocks skapas.
För icke anropsbara mocks kommer den anropsbara varianten att användas (snarare än någon anpassad subklass).
- called¶
En boolean som anger om mock-objektet har anropats eller inte:
>>> mock = Mock(return_value=None) >>> mock.called False >>> mock() >>> mock.called True
- call_count¶
Ett heltal som talar om hur många gånger mock-objektet har anropats:
>>> mock = Mock(return_value=None) >>> mock.call_count 0 >>> mock() >>> mock() >>> mock.call_count 2
- return_value¶
Ställ in detta för att konfigurera det värde som returneras när du anropar mock:
>>> mock = Mock() >>> mock.return_value = 'fish' >>> mock() 'fish'
Standardreturvärdet är ett mock-objekt och du kan konfigurera det på vanligt sätt:
>>> mock = Mock() >>> mock.return_value.attribute = sentinel.Attribute >>> mock.return_value() <Mock name='mock()()' id='...'> >>> mock.return_value.assert_called_with()
return_value
kan också ställas in i konstruktorn:>>> mock = Mock(return_value=3) >>> mock.return_value 3 >>> mock() 3
- side_effect¶
Detta kan antingen vara en funktion som ska anropas när mock anropas, en iterabel eller ett undantag (klass eller instans) som ska tas upp.
Om du skickar in en funktion kommer den att anropas med samma argument som mock och om inte funktionen returnerar
DEFAULT
singleton kommer anropet till mock att returnera det som funktionen returnerar. Om funktionen returnerarDEFAULT
så kommer mock att returnera sitt normala värde (frånreturn_value
).Om du skickar in en iterable används den för att hämta en iterator som måste ge ett värde vid varje anrop. Detta värde kan antingen vara en undantagsinstans som ska aktiveras eller ett värde som ska returneras från anropet till mock (
DEFAULT
-hanteringen är identisk med funktionsfallet).Ett exempel på en mock som ger upphov till ett undantag (för att testa undantagshanteringen i ett API):
>>> mock = Mock() >>> mock.side_effect = Exception('Boom!') >>> mock() Traceback (most recent call last): ... Exception: Boom!
Använda
side_effect
för att returnera en sekvens av värden:>>> mock = Mock() >>> mock.side_effect = [3, 2, 1] >>> mock(), mock(), mock() (3, 2, 1)
Använda en köpbar enhet:
>>> mock = Mock(return_value=3) >>> def side_effect(*args, **kwargs): ... return DEFAULT ... >>> mock.side_effect = side_effect >>> mock() 3
side_effect
kan ställas in i konstruktören. Här är ett exempel som lägger till en till det värde som mocken anropas med och returnerar det:>>> side_effect = lambda value: value + 1 >>> mock = Mock(side_effect=side_effect) >>> mock(3) 4 >>> mock(-8) -7
Om du ställer in
side_effect
tillNone
raderas den:>>> m = Mock(side_effect=KeyError, return_value=3) >>> m() Traceback (most recent call last): ... KeyError >>> m.side_effect = None >>> m() 3
- call_args¶
Detta är antingen
None
(om mock inte har anropats), eller de argument som mock senast anropades med. Detta kommer att vara i form av en tupel: den första medlemmen, som också kan nås via egenskapenargs
, är alla ordnade argument som mocken anropades med (eller en tom tupel) och den andra medlemmen, som också kan nås via egenskapenkwargs
, är alla nyckelordsargument (eller en tom ordbok).>>> mock = Mock(return_value=None) >>> print(mock.call_args) None >>> mock() >>> mock.call_args call() >>> mock.call_args == () True >>> mock(3, 4) >>> mock.call_args call(3, 4) >>> mock.call_args == ((3, 4),) True >>> mock.call_args.args (3, 4) >>> mock.call_args.kwargs {} >>> mock(3, 4, 5, key='fish', next='w00t!') >>> mock.call_args call(3, 4, 5, key='fish', next='w00t!') >>> mock.call_args.args (3, 4, 5) >>> mock.call_args.kwargs {'key': 'fish', 'next': 'w00t!'}
call_args
, tillsammans med medlemmar av listornacall_args_list
,method_calls
ochmock_calls
ärcall
-objekt. Dessa är tupler, så de kan packas upp för att komma åt de enskilda argumenten och göra mer komplexa påståenden. Se calls as tuples.Ändrad i version 3.8: Lagt till egenskaperna
args
ochkwargs
.
- call_args_list¶
Detta är en lista över alla anrop som gjorts till mock-objektet i sekvens (så längden på listan är antalet gånger det har anropats). Innan några anrop har gjorts är det en tom lista. Objektet
call
kan användas för att på ett enkelt sätt konstruera listor över anrop som kan jämföras medcall_args_list
.>>> mock = Mock(return_value=None) >>> mock() >>> mock(3, 4) >>> mock(key='fish', next='w00t!') >>> mock.call_args_list [call(), call(3, 4), call(key='fish', next='w00t!')] >>> expected = [(), ((3, 4),), ({'key': 'fish', 'next': 'w00t!'},)] >>> mock.call_args_list == expected True
Medlemmar i
call_args_list
ärcall
-objekt. Dessa kan packas upp som tupler för att komma åt de enskilda argumenten. Se anrop som tuvor.
- method_calls¶
Förutom att spåra anrop till sig själva, spårar mocks också anrop till metoder och attribut, och deras metoder och attribut:
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.property.method.attribute() <Mock name='mock.property.method.attribute()' id='...'> >>> mock.method_calls [call.method(), call.property.method.attribute()]
Medlemmar i
method_calls
ärcall
-objekt. Dessa kan packas upp som tupler för att komma åt de enskilda argumenten. Se calls som tuvor.
- mock_calls¶
mock_calls
registrerar alla anrop till mock-objektet, dess metoder, magiska metoder och return value mocks.>>> mock = MagicMock() >>> result = mock(1, 2, 3) >>> mock.first(a=3) <MagicMock name='mock.first()' id='...'> >>> mock.second() <MagicMock name='mock.second()' id='...'> >>> int(mock) 1 >>> result(1) <MagicMock name='mock()()' id='...'> >>> expected = [call(1, 2, 3), call.first(a=3), call.second(), ... call.__int__(), call()(1)] >>> mock.mock_calls == expected True
Medlemmar i
mock_calls
ärcall
-objekt. Dessa kan packas upp som tupler för att komma åt de enskilda argumenten. Se calls som tuvor.Anteckning
Sättet som
mock_calls
registreras på innebär att när nästlade anrop görs, registreras inte parametrarna för förfädernas anrop och de kommer därför alltid att jämföras lika:>>> mock = MagicMock() >>> mock.top(a=3).bottom() <MagicMock name='mock.top().bottom()' id='...'> >>> mock.mock_calls [call.top(a=3), call.top().bottom()] >>> mock.mock_calls[-1] == call.top(a=-1).bottom() True
- __class__¶
Normalt kommer attributet
__class__
för ett objekt att returnera dess typ. För ett låtsasobjekt medspec
returnerar__class__
istället spec-klassen. Detta gör att låtsasobjekt kan klaraisinstance()
-tester för det objekt som de ersätter/maskerar sig som:>>> mock = Mock(spec=3) >>> isinstance(mock, int) True
__class__
är tilldelningsbar till, tillåter detta en mock att klara enisinstance()
kontroll utan att tvinga dig att använda en spec:>>> mock = Mock() >>> mock.__class__ = dict >>> isinstance(mock, dict) True
- class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs)¶
En icke anropsbar version av
Mock
. Parametrarna i konstruktören har samma betydelse som iMock
, med undantag för return_value och side_effect som inte har någon betydelse för en icke anropsbar mock.
Låtsasobjekt som använder en klass eller en instans som spec
eller spec_set
kan klara isinstance()
-tester:
>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True
Klasserna Mock
har stöd för mocking av magiska metoder. Se magiska metoder för fullständiga detaljer.
Mock-klasserna och patch()
-dekoratorerna tar alla godtyckliga nyckelordsargument för konfiguration. För patch()
-dekoratorerna skickas nyckelorden till konstruktören för den mock som skapas. Nyckelordsargumenten är till för att konfigurera attribut hos låtsasobjektet:
>>> m = MagicMock(attribute=3, other='fish')
>>> m.attribute
3
>>> m.other
'fish'
Returvärdet och bieffekten för underordnade mocks kan anges på samma sätt, med hjälp av prickad notation. Eftersom du inte kan använda punktnamn direkt i ett anrop måste du skapa en ordbok och packa upp den med **
:
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
...
KeyError
En anropsbar mock som skapades med en spec (eller en spec_set) kommer att introspektera specifikationsobjektets signatur när den matchar anrop till mocken. Därför kan den matcha det faktiska anropets argument oavsett om de skickades positionellt eller med namn:
>>> def f(a, b, c): pass
...
>>> mock = Mock(spec=f)
>>> mock(1, 2, c=3)
<Mock name='mock()' id='140161580456576'>
>>> mock.assert_called_with(1, 2, 3)
>>> mock.assert_called_with(a=1, b=2, c=3)
Detta gäller för assert_called_with()
, assert_called_once_with()
, assert_has_calls()
och assert_any_call()
. När Autospeccing, kommer det också att gälla för metodanrop på mock-objektet.
Ändrad i version 3.4: Lagt till signaturintrospektion på specificerade och autospecerade mock-objekt.
- class unittest.mock.PropertyMock(*args, **kwargs)¶
En mock avsedd att användas som en
property
, eller annan descriptor, på en klass.PropertyMock
tillhandahåller metoderna__get__()
och__set__()
så att du kan ange ett returvärde när den hämtas.Att hämta en
PropertyMock
-instans från ett objekt anropar mocken, utan args. Om den ställs in anropas mocken med det värde som ställs in.>>> class Foo: ... @property ... def foo(self): ... return 'something' ... @foo.setter ... def foo(self, value): ... pass ... >>> with patch('__main__.Foo.foo', new_callable=PropertyMock) as mock_foo: ... mock_foo.return_value = 'mockity-mock' ... this_foo = Foo() ... print(this_foo.foo) ... this_foo.foo = 6 ... mockity-mock >>> mock_foo.mock_calls [call(), call(6)]
På grund av det sätt som mock-attribut lagras kan du inte direkt koppla en PropertyMock
till ett mock-objekt. Istället kan du koppla det till mock-objektet:
>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()
Försiktighet
Om ett AttributeError
uppstår i PropertyMock
tolkas det som att en deskriptor saknas och __getattr__()
anropas på den överordnade mock:
>>> m = MagicMock()
>>> no_attribute = PropertyMock(side_effect=AttributeError)
>>> type(m).my_property = no_attribute
>>> m.my_property
<MagicMock name='mock.my_property' id='140165240345424'>
Se __getattr__()
för mer information.
- class unittest.mock.AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶
En asynkron version av
MagicMock
. ObjektetAsyncMock
kommer att bete sig så att objektet känns igen som en asynkron funktion och resultatet av ett anrop är en awaitable.>>> mock = AsyncMock() >>> inspect.iscoroutinefunction(mock) True >>> inspect.isawaitable(mock()) True
Resultatet av
mock()
är en asynkron funktion som kommer att få resultatetside_effect
ellerreturn_value
efter att den har väntat:om
side_effect
är en funktion, kommer async-funktionen att returnera resultatet av den funktionen,om
side_effect
är ett undantag, kommer async-funktionen att lyfta undantaget,om
side_effect
är en iterabel, kommer async-funktionen att returnera nästa värde i iterabeln, men om resultatsekvensen är uttömd, kommerStopAsyncIteration
att aktiveras omedelbart,om
side_effect
inte är definierad, kommer async-funktionen att returnera det värde som definieras avreturn_value
, därför returnerar async-funktionen som standard ett nyttAsyncMock
-objekt.
Om du anger spec för en
Mock
ellerMagicMock
till en asynkron funktion kommer ett coroutine-objekt att returneras efter anropet.>>> async def async_func(): pass ... >>> mock = MagicMock(async_func) >>> mock <MagicMock spec='function' id='...'> >>> mock() <coroutine object AsyncMockMixin._mock_call at ...>
Om du anger spec för en
Mock
,MagicMock
ellerAsyncMock
till en klass med asynkrona och synkrona funktioner kommer synkrona funktioner automatiskt att identifieras och anges somMagicMock
(om den överordnade mocken ärAsyncMock
ellerMagicMock
) ellerMock
(om den överordnade mocken ärMock
). Alla asynkrona funktioner kommer att varaAsyncMock
.>>> class ExampleClass: ... def sync_foo(): ... pass ... async def async_foo(): ... pass ... >>> a_mock = AsyncMock(ExampleClass) >>> a_mock.sync_foo <MagicMock name='mock.sync_foo' id='...'> >>> a_mock.async_foo <AsyncMock name='mock.async_foo' id='...'> >>> mock = Mock(ExampleClass) >>> mock.sync_foo <Mock name='mock.sync_foo' id='...'> >>> mock.async_foo <AsyncMock name='mock.async_foo' id='...'>
Tillagd i version 3.8.
- assert_awaited()¶
Bekräftar att mocken var väntad minst en gång. Observera att detta är separat från att objektet har anropats, nyckelordet
await
måste användas:>>> mock = AsyncMock() >>> async def main(coroutine_mock): ... await coroutine_mock ... >>> coroutine_mock = mock() >>> mock.called True >>> mock.assert_awaited() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited. >>> asyncio.run(main(coroutine_mock)) >>> mock.assert_awaited()
- assert_awaited_once()¶
Påstå att mocken väntades exakt en gång.
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.assert_awaited_once() >>> asyncio.run(main()) >>> mock.assert_awaited_once() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
- assert_awaited_with(*args, **kwargs)¶
Försäkra dig om att den senaste await var med de angivna argumenten.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_with('foo', bar='bar') >>> mock.assert_awaited_with('other') Traceback (most recent call last): ... AssertionError: expected await not found. Expected: mock('other') Actual: mock('foo', bar='bar')
- assert_awaited_once_with(*args, **kwargs)¶
Bekräftar att mocken väntades exakt en gång och med de angivna argumenten.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
- assert_any_await(*args, **kwargs)¶
Bekräfta att mock någonsin har väntats med de angivna argumenten.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> asyncio.run(main('hello')) >>> mock.assert_any_await('foo', bar='bar') >>> mock.assert_any_await('other') Traceback (most recent call last): ... AssertionError: mock('other') await not found
- assert_has_awaits(calls, any_order=False)¶
Bekräftar att mock har blivit awaited med de angivna anropen. Listan
await_args_list
kontrolleras för awaits.Om any_order är false måste väntetiderna vara sekventiella. Det kan finnas extra anrop före eller efter den angivna väntan.
Om any_order är true kan awaits vara i vilken ordning som helst, men de måste alla finnas med i
await_args_list
.>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> calls = [call("foo"), call("bar")] >>> mock.assert_has_awaits(calls) Traceback (most recent call last): ... AssertionError: Awaits not found. Expected: [call('foo'), call('bar')] Actual: [] >>> asyncio.run(main('foo')) >>> asyncio.run(main('bar')) >>> mock.assert_has_awaits(calls)
- assert_not_awaited()¶
Påstå att spottet aldrig var väntat.
>>> mock = AsyncMock() >>> mock.assert_not_awaited()
- reset_mock(*args, **kwargs)¶
Se
Mock.reset_mock()
. Sätter ävenawait_count
till 0,await_args
till None, och rensarawait_args_list
.
- await_count¶
Ett heltal som håller reda på hur många gånger mock-objektet har väntats på.
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.await_count 1 >>> asyncio.run(main()) >>> mock.await_count 2
- await_args¶
Detta är antingen
None
(om mocken inte har väntats på), eller de argument som mocken senast väntades på med. Fungerar på samma sätt somMock.call_args
.>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args >>> asyncio.run(main('foo')) >>> mock.await_args call('foo') >>> asyncio.run(main('bar')) >>> mock.await_args call('bar')
- await_args_list¶
Detta är en lista över alla väntan som gjorts på mock-objektet i sekvens (så längden på listan är antalet gånger det har väntat). Innan någon väntan har gjorts är det en tom lista.
>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args_list [] >>> asyncio.run(main('foo')) >>> mock.await_args_list [call('foo')] >>> asyncio.run(main('bar')) >>> mock.await_args_list [call('foo'), call('bar')]
- class unittest.mock.ThreadingMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, *, timeout=UNSET, **kwargs)¶
En version av
MagicMock
för multithreading-tester. ObjektetThreadingMock
tillhandahåller extra metoder för att vänta på att ett anrop ska anropas, i stället för att göra en assert på det omedelbart.Standardtimeouten anges av argumentet
timeout
, eller om den inte anges av attributetThreadingMock.DEFAULT_TIMEOUT
, som har blockering (None
) som standard.Du kan konfigurera den globala standardtimeouten genom att ställa in
ThreadingMock.DEFAULT_TIMEOUT
.- wait_until_called(*, timeout=UNSET)¶
Väntar tills mocken anropas.
Om en timeout angavs när mock skapades eller om ett timeout-argument anges till denna funktion, ger funktionen upphov till ett
AssertionError
om anropet inte utförs i tid.>>> mock = ThreadingMock() >>> thread = threading.Thread(target=mock) >>> thread.start() >>> mock.wait_until_called(timeout=1) >>> thread.join()
- wait_until_any_call_with(*args, **kwargs)¶
Väntar tills mock anropas med de angivna argumenten.
Om en tidsgräns angavs vid skapandet av mocken ger funktionen upphov till ett
AssertionError
om anropet inte utförs i tid.>>> mock = ThreadingMock() >>> thread = threading.Thread(target=mock, args=("arg1", "arg2",), kwargs={"arg": "thing"}) >>> thread.start() >>> mock.wait_until_any_call_with("arg1", "arg2", arg="thing") >>> thread.join()
- DEFAULT_TIMEOUT¶
Global standardtimeout i sekunder för att skapa instanser av
ThreadingMock
.
Tillagd i version 3.13.
Ringer¶
Mock-objekt är anropsbara. Anropet kommer att returnera det värde som anges som attributet return_value
. Standardreturvärdet är ett nytt Mock-objekt; det skapas första gången returvärdet används (antingen explicit eller genom att anropa Mock) - men det lagras och samma värde returneras varje gång.
Anrop som görs till objektet registreras i attribut som call_args
och call_args_list
.
Om side_effect
är inställd kommer den att anropas efter att anropet har spelats in, så om side_effect
ger upphov till ett undantag spelas anropet fortfarande in.
Det enklaste sättet att få en mock att skapa ett undantag när den anropas är att göra side_effect
till en undantagsklass eller -instans:
>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]
Om side_effect
är en funktion så är det som funktionen returnerar det som anrop till mock returnerar. Funktionen side_effect
anropas med samma argument som mock. Detta gör att du kan variera anropets returvärde dynamiskt, baserat på indata:
>>> def side_effect(value):
... return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]
Om du vill att mocken fortfarande ska returnera standardreturvärdet (en ny mock), eller något inställt returvärde, finns det två sätt att göra detta. Antingen returnera return_value
från insidan av side_effect
, eller returnera DEFAULT
:
>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
... return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
... return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3
För att ta bort en side_effect
och återgå till standardbeteendet, sätt side_effect
till None
:
>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
... return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6
side_effect
kan också vara ett valfritt iterabelt objekt. Upprepade anrop till mocken kommer att returnera värden från iterabeln (tills iterabeln är uttömd och en StopIteration
utlöses):
>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
...
StopIteration
Om några medlemmar i iterable är undantag kommer de att tas upp istället för returneras:
>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
...
ValueError
>>> m()
66
Radering av attribut¶
Mock-objekt skapar attribut på begäran. Detta gör att de kan låtsas vara objekt av vilken typ som helst.
Du kanske vill att ett mock-objekt ska returnera False
till ett hasattr()
-anrop, eller ge upphov till ett AttributeError
när ett attribut hämtas. Du kan göra detta genom att tillhandahålla ett objekt som en spec
för en mock, men det är inte alltid bekvämt.
Du ”blockerar” attribut genom att radera dem. När ett attribut har tagits bort kommer åtkomst till det att ge upphov till ett AttributeError
.
>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
Låtsasnamn och name-attributet¶
Eftersom ”name” är ett argument till Mock
-konstruktören kan du inte bara skicka in det vid skapandet om du vill att ditt mock-objekt ska ha ett ”name”-attribut. Det finns två alternativ. Ett alternativ är att använda configure_mock()
:
>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'
Ett enklare alternativ är att helt enkelt ställa in attributet ”name” efter skapandet av mock:
>>> mock = MagicMock()
>>> mock.name = "foo"
Bifoga mocks som attribut¶
När du lägger till en mock som ett attribut till en annan mock (eller som returvärde) blir den ett ”barn” till den mocken. Anrop till barnet registreras i attributen method_calls
och mock_calls
hos föräldern. Detta är användbart för att konfigurera barnsocklar och sedan koppla dem till föräldern, eller för att kopplaocklar till en förälder som registrerar alla anrop till barnen och låter dig göra påståenden om anropsordningen mellanocklar:
>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]
Undantaget till detta är om mock har ett namn. Detta gör att du kan förhindra ”föräldraskapet” om du av någon anledning inte vill att det ska hända.
>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]
Mockar som skapas åt dig av patch()
får automatiskt namn. För att koppla mockar som har namn till en förälder använder du metoden attach_mock()
:
>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
... with patch('__main__.thing2', return_value=None) as child2:
... parent.attach_mock(child1, 'child1')
... parent.attach_mock(child2, 'child2')
... child1('one')
... child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
Lapparna¶
Patch-dekoratorerna används för att patcha objekt endast inom ramen för den funktion som de dekorerar. De hanterar automatiskt avpatchningen åt dig, även om undantag uppstår. Alla dessa funktioner kan också användas i with-satser eller som klassdekoratorer.
lappa¶
Anteckning
Nyckeln är att göra patchningen i rätt namnrymd. Se avsnittet Var ska man patcha.
- unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶
patch()
fungerar som en funktionsdekorator, klassdekorator eller en kontexthanterare. Inuti funktionens kropp eller med-satsen patchas målet med ett nytt objekt. När funktionen/med-satsen avslutas ångras patchen.Om new utelämnas, ersätts målet med en
AsyncMock
om det patchade objektet är en asynkron funktion eller enMagicMock
annars. Ompatch()
används som en dekorator och new utelämnas, skickas den skapade mocken in som ett extra argument till den dekorerade funktionen. Ompatch()
används som en kontexthanterare returneras den skapade attrappen av kontexthanteraren.target ska vara en sträng i formen
'package.module.ClassName'
. target importeras och det angivna objektet ersätts med det nya objektet, så target måste vara importerbart från den miljö du anroparpatch()
från. Målet importeras när den dekorerade funktionen exekveras, inte vid dekorationstillfället.Nyckelordsargumenten spec och spec_set skickas till
MagicMock
om patch skapar en åt dig.Dessutom kan du skicka
spec=True
ellerspec_set=True
, vilket gör att patch skickar in objektet som mockas som spec/spec_set-objektet.new_callable låter dig ange en annan klass, eller ett anropbart objekt, som kommer att anropas för att skapa det nya objektet. Som standard används
AsyncMock
för asynkrona funktioner ochMagicMock
för resten.En mer kraftfull form av spec är autospec. Om du anger
autospec=True
kommer mock att skapas med en specifikation från det objekt som ersätts. Alla attribut i mocken kommer också att ha specifikationen för motsvarande attribut i det objekt som ersätts. Metoder och funktioner som mockas kommer att få sina argument kontrollerade och kommer att ge upphov till ettTypeError
om de anropas med fel signatur. För mockar som ersätter en klass kommer deras returvärde (instansen) att ha samma specifikation som klassen. Se funktionencreate_autospec()
och Autospeccing.Istället för
autospec=True
kan du skickaautospec=some_object
för att använda ett godtyckligt objekt som specifikation istället för det som ersätts.Som standard kommer
patch()
att misslyckas med att ersätta attribut som inte existerar. Om du skickar increate=True
, och attributet inte existerar, kommer patch att skapa attributet åt dig när den patchade funktionen anropas, och ta bort det igen efter att den patchade funktionen har avslutats. Detta är användbart för att skriva tester mot attribut som din produktionskod skapar vid körning. Det är avstängt som standard eftersom det kan vara farligt. Med den påslagen kan du skriva passande tester mot API:er som faktiskt inte existerar!Anteckning
Ändrad i version 3.5: Om du patchar inbyggda program i en modul behöver du inte ange
create=True
, det läggs till som standard.Patch kan användas som en
TestCase
klassdekorator. Den fungerar genom att dekorera varje testmetod i klassen. Detta minskar boilerplate-koden när dina testmetoder delar en gemensam patchningsuppsättning.patch()
hittar tester genom att leta efter metodnamn som börjar medpatch.TEST_PREFIX
. Som standard är detta'test'
, vilket matchar det sätt somunittest
hittar tester på. Du kan ange ett alternativt prefix genom att ställa inpatch.TEST_PREFIX
.Patch kan användas som en kontexthanterare, med with-satsen. Här gäller patchen det indragna blocket efter with-satsen. Om du använder ”as” kommer det patchade objektet att bindas till namnet efter ”as”; mycket användbart om
patch()
skapar ett mock-objekt åt dig.patch()
tar godtyckliga nyckelordsargument. Dessa kommer att skickas tillAsyncMock
om det patchade objektet är asynkront, tillMagicMock
annars eller till new_callable om det anges.patch.dict(...)
,patch.multiple(...)
ochpatch.object(...)
finns tillgängliga för alternativa användningsfall.
patch()
som funktionsdekorator, skapar mock åt dig och skickar den till den dekorerade funktionen:
>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
... print(mock_class is SomeClass)
...
>>> function(None)
True
Patching av en klass ersätter klassen med en MagicMock
instans. Om klassen instansieras i koden som testas kommer det att vara return_value
i mocken som kommer att användas.
Om klassen instansieras flera gånger kan du använda side_effect
för att returnera en ny mock varje gång. Alternativt kan du ställa in return_value till att vara vad du vill.
För att konfigurera returvärden på metoder för instances på den patchade klassen måste du göra detta på return_value
. Till exempel:
>>> class Class:
... def method(self):
... pass
...
>>> with patch('__main__.Class') as MockClass:
... instance = MockClass.return_value
... instance.method.return_value = 'foo'
... assert Class() is instance
... assert Class().method() == 'foo'
...
Om du använder spec eller spec_set och patch()
ersätter en class, kommer returvärdet för den skapade mocken att ha samma spec.
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
Argumentet new_callable är användbart om du vill använda en alternativ klass till standardklassen MagicMock
för den skapade mocken. Till exempel, om du vill att en NonCallableMock
ska användas:
>>> thing = object()
>>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
... assert thing is mock_thing
... thing()
...
Traceback (most recent call last):
...
TypeError: 'NonCallableMock' object is not callable
Ett annat användningsfall kan vara att ersätta ett objekt med en io.StringIO
-instans:
>>> from io import StringIO
>>> def foo():
... print('Something')
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
... foo()
... assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()
När patch()
skapar en mock åt dig är det vanligt att det första du behöver göra är att konfigurera mocken. En del av den konfigurationen kan göras i anropet till patch. Alla godtyckliga nyckelord som du skickar in i anropet kommer att användas för att ställa in attribut på den skapade mock:
>>> patcher = patch('__main__.thing', first='one', second='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'one'
>>> mock_thing.second
'two'
Förutom attribut på den skapade mocken kan även attribut, som return_value
och side_effect
, för underliggande mockar konfigureras. Dessa är inte syntaktiskt giltiga att skicka in direkt som nyckelordsargument, men en dictionary med dessa som nycklar kan ändå expanderas till ett patch()
-anrop med hjälp av **
:
>>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> patcher = patch('__main__.thing', **config)
>>> mock_thing = patcher.start()
>>> mock_thing.method()
3
>>> mock_thing.other()
Traceback (most recent call last):
...
KeyError
Som standard kommer försök att patcha en funktion i en modul (eller en metod eller ett attribut i en klass) som inte finns att misslyckas med AttributeError
:
>>> @patch('sys.non_existing_attribute', 42)
... def test():
... assert sys.non_existing_attribute == 42
...
>>> test()
Traceback (most recent call last):
...
AttributeError: <module 'sys' (built-in)> does not have the attribute 'non_existing_attribute'
men genom att lägga till create=True
i anropet till patch()
kommer det föregående exemplet att fungera som förväntat:
>>> @patch('sys.non_existing_attribute', 42, create=True)
... def test(mock_stdout):
... assert sys.non_existing_attribute == 42
...
>>> test()
patch.objekt¶
- patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶
patch den namngivna medlemmen (attribut) på ett objekt (mål) med ett låtsasobjekt.
patch.object()
kan användas som en dekorator, klassdekorator eller kontexthanterare. Argumenten new, spec, create, spec_set, autospec och new_callable har samma betydelse som förpatch()
. Precis sompatch()
tarpatch.object()
godtyckliga nyckelordsargument för att konfigurera det låtsasobjekt som skapas.När det används som en klassdekorator
patch.object()
hedrarpatch.TEST_PREFIX
för att välja vilka metoder som ska omslutas.
Du kan antingen anropa patch.object()
med tre argument eller med två argument. Formen med tre argument tar objektet som ska patchas, attributnamnet och objektet som ska ersätta attributet med.
När du anropar med tvåargumentformen utelämnar du ersättningsobjektet, och en mock skapas åt dig och skickas in som ett extra argument till den dekorerade funktionen:
>>> @patch.object(SomeClass, 'class_method')
... def test(mock_method):
... SomeClass.class_method(3)
... mock_method.assert_called_with(3)
...
>>> test()
spec, create och de andra argumenten till patch.object()
har samma betydelse som de har för patch()
.
patch.dict¶
- patch.dict(in_dict, values=(), clear=False, **kwargs)¶
Patch en ordbok, eller ett ordboksliknande objekt, och återställ ordboken till dess ursprungliga skick efter testet, där den återställda ordboken är en kopia av ordboken som den såg ut före testet.
in_dict kan vara en ordbok eller en mappningsliknande behållare. Om det är en mappning måste den åtminstone stödja hämtning, inställning och borttagning av objekt samt iterering över nycklar.
in_dict kan också vara en sträng som anger namnet på ordboken, som sedan hämtas genom att importera den.
values kan vara en ordlista med värden som ska anges i ordlistan. values kan också vara en iterabel av paren
(nyckel, värde)
.Om clear är true kommer ordlistan att tömmas innan de nya värdena sätts.
patch.dict()
kan också anropas med godtyckliga nyckelordsargument för att ställa in värden i ordlistan.Ändrad i version 3.8:
patch.dict()
returnerar nu den patchade ordlistan när den används som en kontexthanterare.
patch.dict()
kan användas som en kontexthanterare, dekorator eller klassdekorator:
>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
... assert foo == {'newkey': 'newvalue'}
...
>>> test()
>>> assert foo == {}
När det används som en klassdekorator patch.dict()
hedrar patch.TEST_PREFIX
(standard till 'test'
) för att välja vilka metoder som ska omslutas:
>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
... def test_sample(self):
... self.assertEqual(os.environ['newkey'], 'newvalue')
Om du vill använda ett annat prefix för ditt test kan du informera patcharna om det genom att ställa in patch.TEST_PREFIX
. För mer information om hur du ändrar värdet på se TEST_PREFIX.
patch.dict()
kan användas för att lägga till medlemmar i en ordbok, eller helt enkelt låta ett test ändra en ordbok och se till att ordboken återställs när testet avslutas.
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
... assert foo == {'newkey': 'newvalue'}
... assert patched_foo == {'newkey': 'newvalue'}
... # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
... patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
... print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ
Nyckelord kan användas i patch.dict()
-anropet för att ange värden i ordlistan:
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
... import mymodule
... mymodule.function('some', 'args')
...
'fish'
patch.dict()
kan användas med ordboksliknande objekt som egentligen inte är ordböcker. De måste åtminstone stödja hämtning, inställning och borttagning av objekt samt antingen iteration eller medlemskapstest. Detta motsvarar de magiska metoderna __getitem__()
, __setitem__()
, __delitem__()
och antingen __iter__()
eller __contains__()
.
>>> class Container:
... def __init__(self):
... self.values = {}
... def __getitem__(self, name):
... return self.values[name]
... def __setitem__(self, name, value):
... self.values[name] = value
... def __delitem__(self, name):
... del self.values[name]
... def __iter__(self):
... return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
... assert thing['one'] == 2
... assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']
patch.multiple¶
- patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶
Utför flera patchar i ett enda anrop. Det tar objektet som ska patchas (antingen som ett objekt eller en sträng för att hämta objektet genom import) och nyckelordsargument för patcharna:
med patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ...
Använd
DEFAULT
som värde om du vill attpatch.multiple()
ska skapa mocks åt dig. I detta fall skickas de skapade mockarna till en dekorerad funktion med nyckelord, och en dictionary returneras närpatch.multiple()
används som kontexthanterare.patch.multiple()
kan användas som en dekorator, klassdekorator eller kontexthanterare. Argumenten spec, spec_set, create, autospec och new_callable har samma betydelse som förpatch()
. Dessa argument kommer att tillämpas på alla patchar som görs avpatch.multiple()
.När det används som en klassdekorator
patch.multiple()
hedrarpatch.TEST_PREFIX
när det gäller att välja vilka metoder som ska omslutas.
Om du vill att patch.multiple()
ska skapa mocks åt dig, kan du använda DEFAULT
som värde. Om du använder patch.multiple()
som en dekorator så skickas de skapade mockarna in i den dekorerade funktionen med nyckelordet
>>> thing = object()
>>> other = object()
>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... assert isinstance(thing, MagicMock)
... assert isinstance(other, MagicMock)
...
>>> test_function()
patch.multiple()
kan nästlas med andra patch
dekoratorer, men lägger argument som skickas med nyckelord efter något av de standardargument som skapas av patch()
:
>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
... assert 'other' in repr(other)
... assert 'thing' in repr(thing)
... assert 'exit' in repr(mock_exit)
...
>>> test_function()
Om patch.multiple()
används som kontexthanterare är det värde som returneras av kontexthanteraren en ordbok där skapade mockar har nyckelordet name:
>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
... assert 'other' in repr(values['other'])
... assert 'thing' in repr(values['thing'])
... assert values['thing'] is thing
... assert values['other'] is other
...
patchmetoder: start och stopp¶
Alla patchers har metoderna start()
och stop()
. Dessa gör det enklare att göra patchar i setUp
metoder eller där man vill göra flera patchar utan att nesta dekoratorer eller med statements.
För att använda dem anropa patch()
, patch.object()
eller patch.dict()
som vanligt och behåll en referens till det returnerade patcher
-objektet. Du kan sedan anropa start()
för att sätta patchen på plats och stop()
för att ångra den.
Om du använder patch()
för att skapa en mock åt dig så kommer den att returneras av anropet till patcher.start
.
>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock
Ett typiskt användningsfall för detta kan vara att göra flera korrigeringar i metoden setUp
i en TestCase
:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... self.patcher1 = patch('package.module.Class1')
... self.patcher2 = patch('package.module.Class2')
... self.MockClass1 = self.patcher1.start()
... self.MockClass2 = self.patcher2.start()
...
... def tearDown(self):
... self.patcher1.stop()
... self.patcher2.stop()
...
... def test_something(self):
... assert package.module.Class1 is self.MockClass1
... assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()
Försiktighet
Om du använder den här tekniken måste du se till att patchningen ”ångras” genom att anropa stop
. Detta kan vara krångligare än du kanske tror, för om ett undantag uppstår i setUp
så anropas inte tearDown
. unittest.TestCase.addCleanup()
gör detta enklare:
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... patcher = patch('package.module.Class')
... self.MockClass = patcher.start()
... self.addCleanup(patcher.stop)
...
... def test_something(self):
... assert package.module.Class is self.MockClass
...
Som en extra bonus behöver du inte längre behålla en referens till patcher
-objektet.
Det är också möjligt att stoppa alla patchar som har startats genom att använda patch.stopall()
.
- patch.stopall()¶
Stoppar alla aktiva patchar. Stoppar endast patchar som startats med
start
.
patch inbyggda komponenter¶
Du kan patcha alla builtins i en modul. Följande exempel patchar inbyggda ord()
:
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
TEST_PREFIX¶
Alla patchers kan användas som klassdekoratorer. När de används på detta sätt omsluter de varje testmetod i klassen. Patcherna känner igen metoder som börjar med 'test'
som testmetoder. Detta är på samma sätt som unittest.TestLoader
hittar testmetoder som standard.
Det är möjligt att du vill använda ett annat prefix för dina tester. Du kan informera patcharna om det olika prefixet genom att ställa in patch.TEST_PREFIX
:
>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
... def foo_one(self):
... print(value)
... def foo_two(self):
... print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3
Nesting Patch Dekoratörer¶
Om du vill utföra flera patchar kan du helt enkelt stapla dekoratorerna på varandra.
Du kan stapla upp flera patchdekorationer med hjälp av detta mönster:
>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
... assert SomeClass.static_method is mock1
... assert SomeClass.class_method is mock2
... SomeClass.static_method('foo')
... SomeClass.class_method('bar')
... return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')
Observera att dekoratorerna appliceras nedifrån och upp. Detta är det vanliga sättet som Python tillämpar dekoratorer på. Ordningen på de skapade mockarna som skickas in i din testfunktion matchar denna ordning.
Var ska man lappa¶
patch()
fungerar genom att (tillfälligt) byta ut det objekt som ett namn pekar på mot ett annat. Det kan finnas många namn som pekar på ett enskilt objekt, så för att patching ska fungera måste du se till att du patchar det namn som används av det system som testas.
Grundprincipen är att man patchar där ett objekt söks upp, vilket inte nödvändigtvis är samma plats som där det definieras. Ett par exempel kan hjälpa till att förtydliga detta.
Tänk dig att vi har ett projekt som vi vill testa med följande struktur:
a.py
-> Definierar SomeClass
b.py
-> från a import SomeClass
-> some_function instansierar SomeClass
Nu vill vi testa some_function
men vi vill mocka ut SomeClass
med hjälp av patch()
. Problemet är att när vi importerar modul b, vilket vi kommer att behöva göra när den importerar SomeClass
från modul a. Om vi använder patch()
för att mocka ut a.SomeClass
så kommer det inte att ha någon effekt på vårt test; modul b har redan en referens till den verkliga SomeClass
och det ser ut som om vår patchning inte hade någon effekt.
Nyckeln är att patcha ut SomeClass
där den används (eller där den slås upp). I det här fallet kommer some_function
faktiskt att leta upp SomeClass
i modul b, där vi har importerat den. Patchen bör se ut som:
@patch('b.SomeClass')
Tänk dock på det alternativa scenariot där modul b istället för from a import SomeClass
gör import a
och some_function
använder a.SomeClass
. Båda dessa importformer är vanliga. I det här fallet söks den klass vi vill patcha upp i modulen och därför måste vi patcha a.SomeClass
istället:
@patch('a.SomeClass')
Patchning av deskriptorer och proxyobjekt¶
Både patch och patch.object patchar och återställer deskriptorer korrekt: klassmetoder, statiska metoder och egenskaper. Du bör patcha dessa på klassen snarare än på en instans. De fungerar också med vissa objekt som proxy-attributåtkomst, som django settings object.
Stöd för MagicMock och magiska metoder¶
Mocking av magiska metoder¶
Mock
stöder mocking av Python-protokollets metoder, även kända som ”magic methods”. Detta gör att mock-objekt kan ersätta containers eller andra objekt som implementerar Python-protokoll.
Eftersom magiska metoder slås upp på ett annat sätt än vanliga metoder [2] har detta stöd implementerats på ett speciellt sätt. Detta innebär att endast specifika magiska metoder stöds. Listan över metoder som stöds innehåller nästan alla. Om det är någon som saknas som du behöver, meddela oss.
Du mockar magiska metoder genom att ställa in den metod du är intresserad av till en funktion eller en mock-instans. Om du använder en funktion så måste den ta self
som första argument [3].
>>> def __str__(self):
... return 'fooble'
...
>>> mock = Mock()
>>> mock.__str__ = __str__
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__str__ = Mock()
>>> mock.__str__.return_value = 'fooble'
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__iter__ = Mock(return_value=iter([]))
>>> list(mock)
[]
Ett användningsfall för detta är för mocking-objekt som används som kontexthanterare i en with
-sats:
>>> mock = Mock()
>>> mock.__enter__ = Mock(return_value='foo')
>>> mock.__exit__ = Mock(return_value=False)
>>> with mock as m:
... assert m == 'foo'
...
>>> mock.__enter__.assert_called_with()
>>> mock.__exit__.assert_called_with(None, None, None)
Anrop till magiska metoder visas inte i method_calls
, men de registreras i mock_calls
.
Anteckning
Om du använder nyckelordsargumentet spec för att skapa en mock kommer försök att ställa in en magisk metod som inte finns i specifikationen att ge upphov till ett AttributeError
.
Den fullständiga listan över magiska metoder som stöds är:
__hash__
,__sizeof__
,__repr__
ochstr__
__dir__
,__format__
och__subklasser__
”rund”, ”golv”, ”trunk” och ”tak
Jämförelser:
__lt__
,__gt__
,__le__
,ge__
,eq__
ochne__
Container-metoder:
__getitem__
,__setitem__
,__delitem__
,__contains__
,__len__
,__iter__
,__reversed__
och__missing__
Kontexthanterare:
__enter__
,__exit__
,__aenter__
och__aexit__
Unära numeriska metoder:
__neg__
,__pos__
och__invert__
De numeriska metoderna (inklusive högerhands- och in-place-varianter):
__add__
,__sub__
,__mul__
,__matmul__
,__truediv__
,__floordiv__
,__mod__
,__divmod__
,__lshift__
,__rshift__
,__and__
,__xor__
,or__
och__pow__
Numeriska omvandlingsmetoder:
__complex__
,__int__
,__float__
och__index__
Deskriptormetoder:
__get__
,__set__
och__delete__
Betning:
__reduce__
,__reduce_ex__
,__getinitargs__
,getnewargs__
,getstate__
ochsetstate__
Representation av filsystemets sökväg:
__fspath__
Asynkrona iterationsmetoder:
__aiter__
och__anext__
Ändrad i version 3.8: Lagt till stöd för os.PathLike.__fspath__()
.
Ändrad i version 3.8: Lagt till stöd för __aenter__
, __aexit__
, __aiter__
och __anext__
.
Följande metoder finns men stöds inte eftersom de antingen används av mock, inte kan ställas in dynamiskt eller kan orsaka problem:
__getattr__
,__setattr__
,__init__
ochnew__
__prepare__
,__instancecheck__
,__subclasscheck__
,del__
Magic Mock¶
Det finns två varianter av MagicMock
: MagicMock
och NonCallableMagicMock
.
- class unittest.mock.MagicMock(*args, **kw)¶
MagicMock
är en subklass avMock
med standardimplementationer av de flesta av magiska metoder. Du kan användaMagicMock
utan att behöva konfigurera de magiska metoderna själv.Parametrarna för konstruktören har samma betydelse som för
Mock
.Om du använder argumenten spec eller spec_set kommer endast magiska metoder som finns i specifikationen att skapas.
- class unittest.mock.NonCallableMagicMock(*args, **kw)¶
En icke anropsbar version av
MagicMock
.Parametrarna i konstruktorn har samma betydelse som för
MagicMock
, med undantag för return_value och side_effect som inte har någon betydelse för en icke anropsbar mock.
De magiska metoderna konfigureras med MagicMock
-objekt, så att du kan konfigurera dem och använda dem på vanligt sätt:
>>> mock = MagicMock()
>>> mock[3] = 'fish'
>>> mock.__setitem__.assert_called_with(3, 'fish')
>>> mock.__getitem__.return_value = 'result'
>>> mock[2]
'result'
Som standard krävs att många av protokollets metoder returnerar objekt av en viss typ. Dessa metoder är förkonfigurerade med ett standardreturvärde, så att de kan användas utan att du behöver göra något om du inte är intresserad av returvärdet. Du kan fortfarande ställa in returvärdet manuellt om du vill ändra standardvärdet.
Metoder och deras standardvärden:
__lt__
:Inte genomförd
__gt__
:NotImplemented
__le__
:NotImplemented
__ge__
:NotImplemented
__int__
:1
__contains__
:False
__len__
:0
__iter__
:iter([])
__exit__
:False
__aexit__
:False
__komplex__
:1j
__float__
:1,0
__bool__
:True
__index__
:1
__hash__
: standardhash för mock__str__
: standardstr för mock__sizeof__
: standardstorlek för mock
Till exempel:
>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False
De två metoderna för likhet, __eq__()
och __ne__()
, är speciella. De gör standardjämförelsen av likhet på identitet, med hjälp av attributet side_effect
, om du inte ändrar deras returvärde till att returnera något annat:
>>> MagicMock() == 3
False
>>> MagicMock() != 3
True
>>> mock = MagicMock()
>>> mock.__eq__.return_value = True
>>> mock == 3
True
Returvärdet för MagicMock.__iter__()
kan vara vilket iterabelt objekt som helst och behöver inte vara en iterator:
>>> mock = MagicMock()
>>> mock.__iter__.return_value = ['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
Om returvärdet är en iterator kommer den att förbrukas om man itererar över den en gång, och efterföljande iterationer kommer att resultera i en tom lista:
>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]
MagicMock
har alla magiska metoder som stöds konfigurerade förutom några av de obskyra och föråldrade. Du kan fortfarande ställa in dessa om du vill.
Magiska metoder som stöds men inte konfigureras som standard i MagicMock
är:
__underklasser__
__dir__
__format__
__get__
,__set__
och__delete__
__omvända__
och__ saknas__
__reduce__
,__reduce_ex__
,__getinitargs__
,getnewargs__
,getstate__
ochsetstate__
__getformat__
Magiska metoder bör slås upp på klassen snarare än på instansen. Olika versioner av Python är inkonsekventa när det gäller att tillämpa denna regel. De protokollmetoder som stöds bör fungera med alla versioner av Python som stöds.
Funktionen är i princip kopplad till klassen, men varje Mock
-instans hålls isolerad från de andra.
Hjälpmedel¶
vaktpost¶
- unittest.mock.sentinel¶
Objektet
sentinel
ger ett bekvämt sätt att tillhandahålla unika objekt för dina tester.Attribut skapas på begäran när du använder deras namn. Om man använder samma attribut returneras alltid samma objekt. De objekt som returneras har en vettig repr så att meddelanden om testfel är läsbara.
Ibland när man testar behöver man testa att ett specifikt objekt skickas som ett argument till en annan metod, eller returneras. Det kan vara vanligt att skapa namngivna sentinel-objekt för att testa detta. sentinel
ger ett bekvämt sätt att skapa och testa identiteten hos objekt som detta.
I det här exemplet apar vi method
så att den returnerar sentinel.some_object
:
>>> real = ProductionClass()
>>> real.method = Mock(name="method")
>>> real.method.return_value = sentinel.some_object
>>> result = real.method()
>>> assert result is sentinel.some_object
>>> result
sentinel.some_object
STANDARD¶
- unittest.mock.DEFAULT¶
Objektet
DEFAULT
är en förskapad sentinel (egentligensentinel.DEFAULT
). Det kan användas avside_effect
funktioner för att indikera att det normala returvärdet skall användas.
ring upp¶
- unittest.mock.call(*args, **kwargs)¶
call()
är ett hjälpobjekt för att göra enklare påståenden, för jämförelse medcall_args
,call_args_list
,mock_calls
ochmethod_calls
.call()
kan också användas medassert_has_calls()
.>>> m = MagicMock(return_value=None) >>> m(1, 2, a='foo', b='bar') >>> m() >>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()] True
- call.call_list()¶
För ett anropsobjekt som representerar flera anrop returnerar
call_list()
en lista över alla mellanliggande anrop samt det slutliga anropet.
call_list
är särskilt användbart för att göra påståenden om ”kedjeanrop”. Ett kedjeanrop är flera anrop på en enda kodrad. Detta resulterar i flera poster i mock_calls
på en mock. Att manuellt konstruera sekvensen av anrop kan vara tråkigt.
call_list()
kan konstruera en sekvens av anrop från samma kedjeanrop:
>>> m = MagicMock()
>>> m(1).method(arg='foo').other('bar')(2.0)
<MagicMock name='mock().method().other()()' id='...'>
>>> kall = call(1).method(arg='foo').other('bar')(2.0)
>>> kall.call_list()
[call(1),
call().method(arg='foo'),
call().method().other('bar'),
call().method().other()(2.0)]
>>> m.mock_calls == kall.call_list()
True
Ett call
-objekt är antingen en tupel av (positional args, keyword args) eller (name, positional args, keyword args) beroende på hur det konstruerades. När du konstruerar dem själv är detta inte särskilt intressant, men de call
-objekt som finns i attributen Mock.call_args
, Mock.call_args_list
och Mock.mock_calls
kan introspekteras för att komma åt de enskilda argument de innehåller.
Objekten call
i Mock.call_args`
och Mock.call_args_list`
är tvådubbletter av (positional args, keyword args) medan objekten call
i Mock.mock_calls`
, tillsammans med dem du konstruerar själv, är tredubbletter av (name, positional args, keyword args).
Du kan använda deras ”tupleness” för att dra ut de enskilda argumenten för mer komplex introspektion och påståenden. De positionella argumenten är en tupel (en tom tupel om det inte finns några positionella argument) och nyckelordsargumenten är en ordbok:
>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> kall.args
(1, 2, 3)
>>> kall.kwargs
{'arg': 'one', 'arg2': 'two'}
>>> kall.args is kall[0]
True
>>> kall.kwargs is kall[1]
True
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg': 'two', 'arg2': 'three'}
>>> name is m.mock_calls[0][0]
True
skapa_autospec¶
- unittest.mock.create_autospec(spec, spec_set=False, instance=False, **kwargs)¶
Skapa ett mock-objekt som använder ett annat objekt som spec. Attribut på mock-objektet kommer att använda motsvarande attribut på spec-objektet som sin spec.
Funktioner eller metoder som hånas kommer att få sina argument kontrollerade för att säkerställa att de anropas med rätt signatur.
Om spec_set är
True
kommer försök att ange attribut som inte finns på spec-objektet att ge upphov till ettAttributeError
.Om en klass används som specifikation kommer returvärdet för mocken (instansen av klassen) att ha samma specifikation. Du kan använda en klass som specifikation för ett instansobjekt genom att ange
instance=True
. Den returnerade mocken kommer endast att vara anropsbar om instanser av mocken är anropsbara.create_autospec()
tar också godtyckliga nyckelordsargument som skickas till konstruktören för den skapade attrappen.
Se Autospeccing för exempel på hur man använder auto-speccing med create_autospec()
och autospec-argumentet till patch()
.
Ändrad i version 3.8: create_autospec()
returnerar nu en AsyncMock
om målet är en asynkron funktion.
ALLA¶
- unittest.mock.ANY¶
Ibland kan du behöva göra påståenden om några av argumenten i ett anrop till mock, men antingen inte bry dig om några av argumenten eller vilja dra ut dem individuellt ur call_args
och göra mer komplexa påståenden om dem.
För att ignorera vissa argument kan du skicka in objekt som jämför lika med allt. Anrop till assert_called_with()
och assert_called_once_with()
kommer då att lyckas oavsett vad som skickades in.
>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)
ANY
kan också användas i jämförelser med anropslistor som mock_calls
:
>>> m = MagicMock(return_value=None)
>>> m(1)
>>> m(1, 2)
>>> m(object())
>>> m.mock_calls == [call(1), call(1, 2), ANY]
True
ANY
är inte begränsad till jämförelser med anropsobjekt och kan därför också användas i testassertions:
klass TestStringMethods(unittest.TestCase):
def test_split(self):
s = 'hej världen'
self.assertEqual(s.split(), ['hello', ANY])
FILTER_DIR¶
- unittest.mock.FILTER_DIR¶
FILTER_DIR
är en variabel på modulnivå som styr hur låtsasobjekt svarar på dir()
. Standardvärdet är True
, som använder filtreringen som beskrivs nedan, för att bara visa användbara medlemmar. Om du inte gillar denna filtrering, eller behöver stänga av den för diagnostiska ändamål, ställ då in mock.FILTER_DIR = False
.
Med filtrering aktiverad visar dir(some_mock)
endast användbara attribut och inkluderar alla dynamiskt skapade attribut som normalt inte skulle visas. Om låtsasobjektet skapades med en spec (eller autospec förstås) visas alla attribut från originalet, även om de inte har använts ännu:
>>> dir(Mock())
['assert_any_call',
'assert_called',
'assert_called_once',
'assert_called_once_with',
'assert_called_with',
'assert_has_calls',
'assert_not_called',
'attach_mock',
...
>>> from urllib import request
>>> dir(Mock(spec=request))
['AbstractBasicAuthHandler',
'AbstractDigestAuthHandler',
'AbstractHTTPHandler',
'BaseHandler',
...
Många av de inte särskilt användbara (privata för Mock
snarare än för det som mockas) understreckade och dubbelt understreckade prefixattributen har filtrerats bort från resultatet av anropet dir()
på en Mock
. Om du inte gillar det här beteendet kan du stänga av det genom att ställa in modulnivåomkopplaren FILTER_DIR
:
>>> from unittest import mock
>>> mock.FILTER_DIR = False
>>> dir(mock.Mock())
['_NonCallableMock__get_return_value',
'_NonCallableMock__get_side_effect',
'_NonCallableMock__return_value_doc',
'_NonCallableMock__set_return_value',
'_NonCallableMock__set_side_effect',
'__call__',
'__class__',
...
Alternativt kan du bara använda vars(my_mock)
(instansmedlemmar) och dir(type(my_mock))
(typmedlemmar) för att kringgå filtreringen oavsett FILTER_DIR
.
mock_open¶
- unittest.mock.mock_open(mock=None, read_data=None)¶
En hjälpfunktion för att skapa en mock för att ersätta användningen av
open()
. Den fungerar föropen()
som anropas direkt eller används som en kontexthanterare.Argumentet mock är det mock-objekt som ska konfigureras. Om
None
(standard) så kommer enMagicMock
att skapas åt dig, med API begränsat till metoder eller attribut som är tillgängliga på standard filhandtag.read_data är en sträng för metoderna
read()
,readline()
ochreadlines()
i filhandtaget som ska returneras. Anrop till dessa metoder kommer att ta data från read_data tills den är tömd. Mockningen av dessa metoder är ganska enkel: varje gång mock anropas spolas read_data tillbaka till början. Om du behöver mer kontroll över de data som du matar in i den testade koden måste du anpassa den här mocken för dig själv. När det inte räcker kan ett av paketen med filsystem i minnet på PyPI erbjuda ett realistiskt filsystem för testning.Ändrad i version 3.4: Lagt till stöd för
readline()
ochreadlines()
. Mock avread()
ändrad till att konsumera read_data istället för att returnera den vid varje anrop.Ändrad i version 3.5: read_data nollställs nu vid varje anrop till mock.
Ändrad i version 3.8: Lade till
__iter__()
till implementationen så att iteration (t.ex. i for-loopar) korrekt förbrukar read_data.
Att använda open()
som en kontexthanterare är ett bra sätt att säkerställa att dina filhandtag stängs ordentligt och blir allt vanligare:
med open('/some/sökväg', 'w') som f:
f.write("något")
Problemet är att även om du mockar ut anropet till open()
är det det återlämnade objektet som används som en kontexthanterare (och har __enter__()
och __exit__()
anropade).
Att mocka kontexthanterare med en MagicMock
är tillräckligt vanligt och krångligt för att en hjälpfunktion är användbar.
>>> m = mock_open()
>>> with patch('__main__.open', m):
... with open('foo', 'w') as h:
... h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
call().__enter__(),
call().write('some stuff'),
call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
Och för att läsa filer:
>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
... with open('foo') as h:
... result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'bibble'
Autospeccing¶
Autospeccing baseras på den befintliga funktionen spec
i mock. Den begränsar api för mocks till api för ett originalobjekt (spec), men den är rekursiv (implementeras lättsinnigt) så att attribut för mocks endast har samma api som attributen för spec. Dessutom har mockade funktioner / metoder samma anropssignatur som originalet så att de ger upphov till ett TypeError
om de anropas felaktigt.
Innan jag förklarar hur autospeccing fungerar ska du få veta varför det behövs.
Mock
är ett mycket kraftfullt och flexibelt objekt, men det lider av ett fel som är generellt för mocking. Om du refaktoriserar en del av din kod, byter namn på medlemmar och så vidare, kommer alla tester för kod som fortfarande använder det gamla api men använder mocks istället för de riktiga objekten fortfarande att godkännas. Detta innebär att alla dina tester kan godkännas även om din kod är trasig.
Ändrad i version 3.5: Före 3.5 kunde tester med ett stavfel i ordet assert godkännas i tysthet när de borde ge upphov till ett fel. Du kan fortfarande uppnå detta beteende genom att skicka unsafe=True
till Mock.
Observera att detta är ytterligare ett skäl till varför du behöver integrationstester såväl som enhetstester. Det är bra att testa allting isolerat, men om du inte testar hur dina enheter är ”ihopkopplade” finns det fortfarande gott om utrymme för buggar som testerna kanske hade fångat upp.
unittest.mock
tillhandahåller redan en funktion som hjälper till med detta, kallad speccing. Om du använder en klass eller instans som spec
för en mock så kan du bara komma åt attribut på mocken som finns på den riktiga klassen:
>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
Specifikationen gäller bara för själva mocken, så vi har fortfarande samma problem med alla metoder på mocken:
>>> mock.header_items()
<mock.Mock object at 0x...>
>>> mock.header_items.assret_called_with() # Intentional typo!
Auto-specificering löser detta problem. Du kan antingen skicka autospec=True
till patch()
/ patch.object()
eller använda funktionen create_autospec()
för att skapa en mock med en spec. Om du använder argumentet autospec=True
till patch()
så kommer objektet som ersätts att användas som spec-objekt. Eftersom specifikationen görs ”lazily” (specifikationen skapas när attribut på mocken nås) kan du använda den med mycket komplexa eller djupt nästlade objekt (som moduler som importerar moduler som importerar moduler) utan en stor prestandahit.
Här är ett exempel på hur det används:
>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>
Du kan se att request.Request
har en spec. request.Request
tar två argument i konstruktorn (varav ett är self). Här är vad som händer om vi försöker anropa den felaktigt:
>>> req = request.Request()
Traceback (most recent call last):
...
TypeError: <lambda>() takes at least 2 arguments (1 given)
Specifikationen gäller också för instansierade klasser (dvs. returvärdet för specificerade mocks):
>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>
Request
-objekt är inte anropsbara, så returvärdet för instantiering av vår mockade request.Request
är en icke anropsbar mock. Med specifikationen på plats kommer eventuella typfel i våra asserts att ge upphov till rätt fel:
>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')
I många fall kommer du bara att kunna lägga till autospec=True
till dina befintliga patch()
-anrop och sedan vara skyddad mot buggar på grund av typfel och api-ändringar.
Förutom att använda autospec genom patch()
finns det en create_autospec()
för att skapa autospecade mockar direkt:
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
Detta är dock inte utan förbehåll och begränsningar, vilket är anledningen till att det inte är standardbeteendet. För att veta vilka attribut som är tillgängliga på spec-objektet måste autospec introspektera (få tillgång till attribut) spec. När du traverserar attribut på mock sker en motsvarande traversering av det ursprungliga objektet under huven. Om något av dina specificerade objekt har egenskaper eller deskriptorer som kan utlösa kodkörning kanske du inte kan använda autospec. Å andra sidan är det mycket bättre att utforma dina objekt så att introspektion är säker [4].
Ett allvarligare problem är att det är vanligt att instansattribut skapas i metoden __init__()
och inte alls finns i klassen. autospec kan inte känna till några dynamiskt skapade attribut och begränsar api:et till synliga attribut.
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Det finns några olika sätt att lösa detta problem. Det enklaste, men inte nödvändigtvis det minst irriterande, sättet är att helt enkelt ställa in de nödvändiga attributen på mock efter skapandet. Bara för att autospec inte tillåter dig att hämta attribut som inte finns i specifikationen hindrar det inte dig från att ställa in dem:
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
Det finns en mer aggressiv version av både spec och autospec som förhindrar att du anger icke-existerande attribut. Detta är användbart om du vill se till att din kod bara ställer in giltiga attribut också, men det förhindrar naturligtvis just detta scenario:
>>> with patch('__main__.Something', autospec=True, spec_set=True):
... thing = Something()
... thing.a = 33
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Förmodligen är det bästa sättet att lösa problemet att lägga till klassattribut som standardvärden för instansmedlemmar som initieras i __init__()
. Observera att om du bara ställer in standardattribut i __init__()
är det också snabbare att tillhandahålla dem via klassattribut (som naturligtvis delas mellan instanser). t.ex.
klass Något:
a = 33
Detta ger upphov till en annan fråga. Det är relativt vanligt att tillhandahålla ett standardvärde av None
för medlemmar som senare kommer att vara ett objekt av en annan typ. None
skulle vara värdelös som en specifikation eftersom det inte skulle låta dig komma åt * några * attribut eller metoder på den. Eftersom None
aldrig kommer att vara användbar som specifikation, och förmodligen indikerar en medlem som normalt kommer att vara av någon annan typ, använder autospec inte en specifikation för medlemmar som är inställda på None
. Dessa kommer bara att vara vanliga mocks (ja - MagicMocks):
>>> class Something:
... member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<MagicMock name='mock.member.foo.bar.baz()' id='...'>
Om du inte gillar att ändra dina produktionsklasser för att lägga till standardvärden finns det fler alternativ. Ett av dessa är att helt enkelt använda en instans som specifikation i stället för klassen. Det andra är att skapa en underklass av produktionsklassen och lägga till standardvärdena i underklassen utan att påverka produktionsklassen. Båda dessa kräver att du använder ett alternativt objekt som spec. Tack och lov stöder patch()
detta - du kan helt enkelt skicka det alternativa objektet som autospec-argument:
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> class SomethingForTest(Something):
... a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
Detta gäller endast klasser eller redan instantierade objekt. Att anropa en mockad klass för att skapa en mockad instans skapar inte en riktig instans. Det är bara attributuppslagningar - tillsammans med anrop till dir()
- som görs.
Försegling av mockar¶
- unittest.mock.seal(mock)¶
Seal inaktiverar det automatiska skapandet av mockar vid åtkomst till ett attribut för den mock som förseglas eller något av dess attribut som redan är mockar rekursivt.
Om en låtsasinstans med ett namn eller en specifikation tilldelas ett attribut kommer den inte att beaktas i tätningskedjan. Detta gör att man kan förhindra att seal fixar en del av mock-objektet:
>>> mock = Mock() >>> mock.submock.attribute1 = 2 >>> mock.not_submock = mock.Mock(name="sample_name") >>> seal(mock) >>> mock.new_attribute # This will raise AttributeError. >>> mock.submock.attribute2 # This will raise AttributeError. >>> mock.not_submock.attribute2 # This won't raise.
Tillagd i version 3.7.
Prioritetsordning för side_effect
, return_value
och wraps¶
Ordningen på deras företräde är:
wraps
Om alla tre är inställda kommer mock att returnera värdet från side_effect
och ignorera return_value
och det omslutna objektet helt och hållet. Om två är inställda, kommer den med högst prioritet att returnera värdet. Oavsett vilken som angavs först förblir prioritetsordningen oförändrad.
>>> from unittest.mock import Mock
>>> class Order:
... @staticmethod
... def get_value():
... return "third"
...
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first'
Eftersom None
är standardvärdet för side_effect
, om du tilldelar dess värde tillbaka till None
, kommer prioritetsordningen att kontrolleras mellan return_value
och det inlindade objektet, utan hänsyn till side_effect
.
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
Om det värde som returneras av side_effect
är DEFAULT
ignoreras det och prioritetsordningen flyttas till efterföljaren för att få det värde som ska returneras.
>>> from unittest.mock import DEFAULT
>>> order_mock.get_value.side_effect = [DEFAULT]
>>> order_mock.get_value()
'second'
När Mock
omsluter ett objekt kommer standardvärdet för return_value
att vara DEFAULT
.
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.return_value
sentinel.DEFAULT
>>> order_mock.get_value.return_value
sentinel.DEFAULT
Prioritetsordningen ignorerar detta värde och går vidare till den sista efterträdaren, vilket är det inplastade objektet.
Eftersom det verkliga anropet görs till det inlindade objektet, kommer skapandet av en instans av denna mock att returnera den verkliga instansen av klassen. De eventuella positionella argument som krävs av det inkapslade objektet måste skickas.
>>> order_mock_instance = order_mock()
>>> isinstance(order_mock_instance, Order)
True
>>> order_mock_instance.get_value()
'third'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'second'
Men om du tilldelar None
till det, kommer detta inte att ignoreras eftersom det är en explicit tilldelning. Så prioritetsordningen kommer inte att flyttas till det inplastade objektet.
>>> order_mock.get_value.return_value = None
>>> order_mock.get_value() is None
True
Även om du ställer in alla tre på en gång när du initierar mocken, förblir prioritetsordningen densamma:
>>> order_mock = Mock(spec=Order, wraps=Order,
... **{"get_value.side_effect": ["first"],
... "get_value.return_value": "second"}
... )
...
>>> order_mock.get_value()
'first'
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
Om side_effect
är uttömd, kommer rangordningen inte att leda till att ett värde erhålls från efterföljarna. Istället utlöses undantaget StopIteration
.
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first side effect value",
... "another side effect value"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first side effect value'
>>> order_mock.get_value()
'another side effect value'
>>> order_mock.get_value()
Traceback (most recent call last):
...
StopIteration