#python #flask #pytest #flask-sqlalchemy
Вопрос:
У меня есть следующий простой тестовый случай, который проверяет операцию удаления:
def test_delete_club(client, systemAdmin, operationalCountry1, club1):
rv = loginTo(client, '/admin/clubs/1', ' 00000000000','testpassword')
decodedRv = rv.data.decode('utf-8')
assert '<td>testClub1</td>' in decodedRv
rv = client.get('/admin/delete_club/1', follow_redirects = True)
decodedRv = rv.data.decode('utf-8')
assert '<td>testClub1</td>' not in decodedRv
#ensure club1 does not exist in the database either
c = Club.query.get(1)
assert c is None
# make sure club roles are deleted along with the club
clubRoles = Role.query.filter(Role.club_id == club1.id).all()
assert len(clubRoles)
В основном, он попадает по URL-адресу удаления ( /admin/delete_club/1
), и после следующих перенаправлений он утверждает, что в базе данных нет ни такого клуба, ни каких-либо ролей, связанных с этим клубом.
Я использую TDD (разработка на основе тестов). Поэтому я написал приведенный выше тестовый пример перед соответствующим кодом маршрутизации. Мой метод маршрута выглядит следующим образом:
@flaskApp.route('/admin/delete_club/<int:id>', methods=['GET'])
@login_required
def delete_club(id):
'''Deletes the club identified by the id'''
if not isCurrentUserSysAdmin():
#TODO better error handling
return 'you can not touch this'
clubToDelete = Club.query.get_or_404(id)
logging.debug('About to delete club: {}'.format(clubToDelete))
opCountryId = clubToDelete.operationalcountry_id
try:
db.session.delete(clubToDelete)
logging.debug('Deleted club: {}'.format(clubToDelete))
except:
flash('Error deleting club')
logging.error('Error deleting club. Club Details: {}'.format(clubToDelete))
return redirect(url_for('clubs', countryid = opCountryId))
Что ж, пока все идет хорошо. Тестовый случай прошел без сбоев. Я был счастлив. Я хотел попробовать это на настоящей веб-странице. Затем я заметил, что, хотя операция удаления удалась успешно, на перенаправленной странице клуб, который я пытался удалить, все еще будет присутствовать.
Затем я обнаружил ОШИБКУ : я просто забыл добавить db.session.commit()
после удаления сущности экземпляра клуба. Это изменение исправило веб-страницу.
Тем не менее, я все еще озадачен, почему мой тестовый случай не жалуется на это и почему он вообще не проваливается? Опять же, тестовый пример работает без оператора commit.
Есть какие-нибудь идеи от более опытных разработчиков Flask/Python?
Ответ №1:
Короче говоря, db.session.delete
зарегистрируйте транзакцию в памяти для выполнения. Но без db.commit
этого не выполнялся бы запрос к базе данных. Этот метод изменит только локальное представление баз данных. Что я рекомендую вам, так это использовать контекстный менеджер with session.begin()
.
Комментарии:
1. Как я уже сказал, я нашел и исправил ошибку, добавив db.session.commit() после заявления об удалении. Однако ваш ответ НЕ проливает никакого света на то, почему тестовый случай работал без инструкции commit.
2. Я имею в виду, я не знаю, что стоит за DB. Но может быть несколько возможностей, таких как область действия приспособления, возможно, некоторые настройки БД, возможно, какой-то флаг алхимии SQL в pytest для автоматической фиксации области действия.
3. Области действия всех моих приборов находятся на функциональном уровне, кроме клиентских. Настройки БД могут иметь значение, поскольку я использую PostgreSQL для создания подражателей и SQLite (в памяти) для разработки. Я присмотрюсь к ним повнимательнее. Спасибо, что указали. Не могли бы вы немного подробнее рассказать об алхимии SQL в pytest для автоматической фиксации области действия? Это произведение ни о чем не говорило.