Каков правильный способ тестирования простых операций CRUD с помощью Pytest, чтобы обеспечить правильное чтение и запись? Возвращается объект функции вместо значения

#pytest #python-3.7 #assert

#пытест #python-3.7 #утверждать

Вопрос:

Используя Pytest , я пытаюсь протестировать простую функцию базы read update данных и функцию. Сначала update необходимо запустить функцию, чтобы убедиться, что в тестовой базе данных есть значения, которые мы будем проверять в нашей read функции.

/src Там мой основной скрипт, вот функции read , update , delete :

 async def read(self, collection, query=None, mods=None):   Table = self._cache.classes[collection]   async with self._session() as session:  matches = set(Table.__mapper__.columns.keys()) amp; set(mods['fields'])   for m in matches:  q = select(getattr(Table, m))  q = q.filter_by(**query)   result = await session.execute(q)   return [row for row in result]   async def update(self, collection, query, data):   Table = self._cache.classes[collection]   async with self._session() as s:  q = sa_update(Table).filter_by(**query)  q = q.values(**data)   await s.execute(q)  await s.commit()   async def delete_collection(self, table_name, syncro_engine=sync_engine):  table = self.retrieve_table_obj(table_name)  async with self._engine.begin() as conn:  await conn.run_sync(Base.metadata.drop_all(syncro_engine, [table], checkfirst=True))  

При запуске read вывод [('Wonderland',)] таков, что это то, в чем я провожу тестирование assert Pytest .

У /test меня есть conftest.py файл, в котором есть эти три функции:

 @pytest.fixture(scope='function') def test_update():  def get_update(collection, query, data):  return asyncio.run(DB.update(collection, query, data))  return get_update   @pytest.fixture(scope='function') def test_read():  def get_read(collection, query, mods):  return asyncio.run(DB.read(collection, query, mods))  return get_read  @pytest.fixture(scope='function') def drop_table():  def get_delete_tables(table_name, sync_engine):  return asyncio.run(DB.delete_collection(table_name, sync_engine))  return get_delete_tables  

Затем тесты запускаются в test_db.py :

 # Tests 'update' by updating user 'Alice' with 'org' 'Wonderland' # Doesn't return anything, using it to ensure the correct values are in test_db to test the read function @pytest.mark.parametrize('function_input, expected',  [("'user', {'name': 'Alice'}, {'org': 'Wonderland'})", "[('Wonderland',)]")]) def test_update(test_update, function_input, expected):  test_update   # Tests 'read' by reading the just-updated values  @pytest.mark.parametrize('function_input, expected',  [("'user', query={'name': 'Alice'}, mods={'fields': ['name', 'org']})", "[('Wonderland',)]")]) def test_read(test_read, function_input, expected):  assert test_read == expected  # Tests that tables are dropped cleanly by dropping a given table then checking for that table name @pytest.mark.parametrize('fn_input1, fn_input2, expected',  [('image', sync_engine, 'image')]) def test_drop_table(drop_table, collection_names, fn_input1, fn_input2, expected):  # First drop the table  drop_table(fn_input1, fn_input2)  # Now check that it no longer exists  with pytest.raises(KeyError) as error_info:  assert fn_input1 in collection_names  raise KeyError  assert error_info.type is KeyError   

Запуск test_drop_table ничего не отбрасывает test_db , даже если ему передается правильная база test_db данных и ее аналог в основном скрипте /src работает должным образом.

Когда я запускаю этот test_read тест, он терпит неудачу:

 gt; assert test_read == expected E assert lt;function test_read.lt;localsgt;.get_read at 0x7f1fa6beed08gt; == "[('Wonderland',)]"  

Как я могу получить доступ к фактическому возвращаемому значению assert «против», т. Е. [('Wonderland',)] , а не lt;function objectgt; «против»?

Комментарии:

1. Когда вы возвращаете объект функции в своем устройстве, вы должны вызвать эту функцию в своем тесте. В качестве отступления — вероятно , не стоит называть ваши приборы test_xxx , тем более, что они имеют то же название, что и фактические тесты.

2. Спасибо! Можете ли вы уточнить, что вы имеете в виду, вызывая эту функцию в моем тесте? Например, внутри conftest.py есть приспособление drop_table , которое вызывает get_delete_tables . Он настроен таким образом, чтобы я мог передавать параметры из test_db.py функции, вызываемой устройством. На самом деле мне не нужно заявление о возврате get_delete_tables . Мне просто нужно получить доступ к вызовам основной функции скрипта ( asyncio.run(DB.delete_collection(table_name, sync_engine)) ) get_delete_tables . Поэтому , чтобы вызвать это приспособление в тесте, я вызываю drop_tables и передаю параметры в «get_delete_tables».

3. Все ваши приборы ничего не делают, они просто возвращают указатель на функцию. В test_drop_table вы на самом деле вызываете функцию, но в других тестах вы этого не делаете. Я на самом деле не понимаю, что должны делать тесты — описание test_update звучит так, как будто это задумано как настройка, хотя в этом случае оно должно быть приспособлением и на самом деле должно что-то делать (в настоящее время оно ничего не делает). Второй тест также ничего не вызывает, он просто сравнивает указатель функции с srguments — я ожидал бы, что вы захотите вызвать функцию вместо этого.

4. Да, похоже, вам здесь не нужно приспособление. Вы можете использовать приспособления без возвращаемого значения, особенно для функций, подобных настройке/демонтажу (где вы выполняете настройку до yield и демонтаж после yield ), но в этом случае вы должны знать аргументы для вызова в приспособлении, которых у вас, вероятно, нет в вашем случае.

5. Да, если под «вызывает это приспособление» вы имеете в виду «поместите его имя в аргументы теста». Существует также @pytest.mark.usefixtures, которые могут использоваться в модуле или классе.