Pytest — исправление класса не работает, вместо этого вызывает класс

Не уверен, почему, но вот мой фрагмент кода:

stats_collector.py

class StatsCollector( object ) :
def __init__( self, user_id, app_id ) :
    logging.info( "Stats: APP_ID = {0}, ID = {1}".format( app_id, user_id ) )

mydb.py

from namespacename.mylibname.stats_collector import StatsCollector
class Db( object ) :
    # constructor/destructor
    def __init__( self, dbname ) :
        ....

    def beginTransaction( self, user_id, app_id ) :
        logging.info( "Begin" )
        self._stats = StatsCollector( user_id, app_id ) 

test_mylibname_mydb

from namespacename.mylibname.mydb import Db
from namespacename.mylibname.stats_collector import StatsCollector
@pytest.mark.mydb_temp
@mock.patch( 'namespacename.mylibname.stats_collector.StatsCollector')
def test_db_beginTransaction( mock_stats_collector ) :
    db = Db( TEST_DB_NAME )
    mock_stats_collector.return_value = mock.Mock()
    db.beginTransaction( TEST_ID, TEST_APP_ID )
    ......
    ......

Я вижу свой журнал в моем stats_collector.__init__ - зачем я это ввожу? Разве внутри моего beginTransaction я не должен вызывать StatsCollector возвращаемое значение как MockObject, и я не должен видеть никаких журналов?

Структура выглядит так:

tests/
├── mylibname
│   ├── test_mylibname_mydb.py
namespacename/mylibname
├── stats_collector
│   ├── mylibname_stats_collector.py
│   ├── __init__.py
├── mydb
│   ├── mylibname_mydb.py
│   ├── __init__.py

** Редактировать **

Следовал совету в комментарии -

@mock.patch( 'namespacename.mylibname.mydb.StatsCollector')
def test_db_beginTransaction( mock_stats_init ) :
    db = Db( TEST_DB_NAME )
    db.beginTransaction( TEST_UUID, TEST_APP_ID )
    print db._transaction
    assert db._transaction is mock_stats_init

получает меня :

E       AssertionError: assert <namespacename.mylibname.stats_collector.mylibname_stats_collector.StatsCollector object at 0x7f42d837b110> is <MagicMock name='StatsCollector' id='139925072008976'>
E        +  where <namespacename.mylibname.stats_collector.mylibname_stats_collector.StatsCollector object at 0x7f42d837b110> = <namespacename.mylibname.mydb.mylibname_mydb.Db object at 0x7f42d8365850>._transaction
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
1 963
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вам необходимо исправить символ «A» в тестируемом модуле, т.е. «B».

Когда вы делаете @mock.patch("full.path.A"), это должно быть:

@mock.patch("full.path.to.B.A")

Теперь символ A в модуле B исправлен вашим макетом.

Я получаю <class full.path.to.B >does not have the attribute A, когда делаю это

ghostrider 09.04.2019 19:09

может быть, это поможет, если вы опубликуете структуру папок, где находятся A и B, и фактический импорт / патчи.

rdas 09.04.2019 19:10
Ответ принят как подходящий

Вы не пропустили имя самого файла?

@mock.patch( 'namespacename.mylibname.stats_collector.mylibname_stats_collector.StatsCollector')

Вы могли бы понять, как я пишу это.

Эмпирическое правило: не исправлять классы или функции там, где они определены, а исправлять их там, где они используются.

a.py

class A:
    def exponent(self, a, b)
        return a ** b
    

b.py    


from a import A
class B:
   def add_constat(a, b, c)
      return A().exponent(a, b) + c
      
  
  
  

Для тестирования метода add_constant у вас может возникнуть соблазн исправить A, как в

TestB:

@patch('a.A.exponent', mock_a)
    test_add_constant(self, mock_a) 

это неправильно, так как вы исправляете класс в файле, где дано его определение.

A используется в файле b в классе B. Следовательно, вместо этого вы должны исправить этот класс.

TestB:

    @patch('b.A')
    test_add_constant(self, mock_a):
    # mock_a is fake class of A, the return_value of mock_a gives us an instance (object) of the class(A)
    instance_a = mock_a.return_value # 
   
    # we now have instance of the class i.e A, hence it is possible to call the methods of class A
   
    instance_a.exponent.return_value = 16
   
    assert 26 = B().add_constant(2,4,10)

Я немного изменил ваш код, чтобы он работал в моей среде Python.

stats_collector.py

class StatsCollector:
    def __init__(self, user_id, app_id):
        self.user_id = user_id
        self.app_id = app_id

    def stat(self):
        return self.user_id + ':' + self.app_id


mydb.py

from stats_collector import StatsCollector
import logging

class Db:
    # constructor
    def __init__(self, db_name):
        self.db_name = db_name

    def begin_transaction(self, user_id, app_id):
        logging.info("Begin")
        stat = StatsCollector(user_id, app_id).stat()

        if stat:
            return user_id + ':' + app_id
        return "wrong User"

используя аналогию: для тестирования «begin_transaction» в классе Db файла mydb.py вам необходимо исправить класс StatsCollector, используемый в файле mydb.py

test_mydb.py

from unittest.mock import patch
from unittest import TestCase


class TestDb(TestCase):

    @patch('mydb.StatsCollector')
    def test_begin_transaction(self, db_class_mock):

        # db_class_mock is the mock of the class, it is not an instance of the DB class.
        # to create an instance of the class, you need to call return_value
       
        db_instance = db_class_mock.return_value
        db_instance.stat.return_value = 1
        # i prefere to do the above two lines in just one line as

        #db_class_mock.return_value.stat.return_value = 1
        

        db = Db('stat')
        expected = db.begin_transaction('Addis', 'Abeba')

        assert expected == 'Addis' + ':' + 'Abeba'

        # set the return value of stat method
        db_class_mock.return_value.stat.return_value = 0
        expected = db.begin_transaction('Addis', 'Abeba')

        assert expected == "wrong User"

Я надеюсь, что это поможет кому-то в сети

Другие вопросы по теме