Знущаються над атрибутами в Python mock?


76

У мене досить складно провести mockз Python:

def method_under_test():
    r = requests.post("http://localhost/post")

    print r.ok # prints "<MagicMock name='post().ok' id='11111111'>"

    if r.ok:
       return StartResult()
    else:
       raise Exception()

class MethodUnderTestTest(TestCase):

    def test_method_under_test(self):
        with patch('requests.post') as patched_post:
            patched_post.return_value.ok = True

            result = method_under_test()

            self.assertEqual(type(result), StartResult,
                "Failed to return a StartResult.")

Тест фактично повертає правильне значення, але r.okє об'єктом Mock, а не True. Як ви глузуєте над атрибутами в mockбібліотеці Python ?

Відповіді:


92

Вам потрібно використовувати return_valueі PropertyMock:

with patch('requests.post') as patched_post:
    type(patched_post.return_value).ok = PropertyMock(return_value=True)

Це означає: під час виклику requests.postна зворотне значення цього виклику встановіть PropertyMockдля властивості okповернення значення True.


Якщо я printзначення r.okз в method_under_test, я бачу <MagicMock name='post().ok' id='57360464'>, ні True.
Naftuli Kay

@TKKocheran: Я оновив свою відповідь. Вам також потрібно використовувати a PropertyMock.
Симеон Віссер

14
Чому б не спростити використання patched_post.return_value = mock.Mock(ok=True)?
смазка

3
@lumbric Оскільки ви можете використовувати, PropertyMockщоб стверджувати, що доступ до нього здійснювався як будь-який інший Mockоб'єкт. Просто призначивши значення властивості, ви цього не зможете зробити.
Боно

20

Компактний і простий спосіб зробити це - використовувати new_callable patchатрибут '' для примусу patchдо використання, PropertyMockа не MagicMockдля створення макетного об'єкта. Інші аргументи, передані, patchбудуть використані для створення PropertyMockоб'єкта.

with patch('requests.post.ok', new_callable=PropertyMock, return_value=True) as mock_post:
    """Your test"""

15

У макетній версії '1.0.1' підтримується простіший синтаксис, згаданий у питанні, і працює як є!

Приклад оновленого коду (замість unittest використовується py.test):

import mock
import requests


def method_under_test():
    r = requests.post("http://localhost/post")

    print r.ok

    if r.ok:
        return r.ok
    else:
        raise Exception()


def test_method_under_test():
    with mock.patch('requests.post') as patched_post:
        patched_post.return_value.ok = True

        result = method_under_test()
        assert result is True, "mock ok failed"

Запустіть цей код за допомогою: (переконайтесь, що ви встановили pytest)

$ py.test -s -v mock_attributes.py 
======= test session starts =======================
platform linux2 -- Python 2.7.10 -- py-1.4.30 -- pytest-2.7.2 -- /home/developer/miniconda/bin/python
rootdir: /home/developer/projects/learn/scripts/misc, inifile: 
plugins: httpbin, cov
collected 1 items 

mock_attributes.py::test_method_under_test True
PASSED

======= 1 passed in 0.03 seconds =================

Це не дає відповіді на запитання. Щоб критикувати або вимагати роз’яснень від автора, залиште коментар під його публікацією - ви завжди можете коментувати власні публікації, і коли у вас буде достатня репутація, ви зможете коментувати будь-яку публікацію .
філант

FYI requests.post.ok- це властивість, а не атрибут. Якщо ви приміряєте простий об'єкт, де okє простий атрибут, синтаксис, згаданий у питанні, працює, але для requests.post.okоб'єкта №: він підніме значення AttributeError.
Мікеле д'Аміко

@philant дякую за ваш відгук, як довів приклад, це сучасна відповідь на запитання, а синтаксис набагато простіший.
howaryoo

@ Micheled'Amico дякую за ваш відгук, я спробував, будь ласка, подивіться ;-)
howaryoo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.