Поведенческая разница между кодом pytest и реальным сеансом flask для маршрута

#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 для автоматической фиксации области действия? Это произведение ни о чем не говорило.