Почему мой клиент-тестер с колбой не работает примерно в 60% случаев? - PullRequest
0 голосов
/ 25 июня 2018

Я свел свою проблему к отдельному приложению с колбой + модульный тест. Когда это выполняется с pytest app.py, оно примерно половину времени дает сбой (29 из 50 запусков) с этой ошибкой:

E           werkzeug.routing.BuildError: Could not build url for endpoint 'thing' with values ['_sa_instance_state']. Did you forget to specify values ['id']?

Огорчает то, что добавление отладочных операторов в методе post() делает его всегда успешным (см. Комментарий ниже).

Это похоже на состояние гонки где-то в рамках. SQLAlchemy порождает поток для выполнения фиксации и обновления t.id?

Я могу заставить его выйти из строя, выполнив del t.id на месте комментария (подтверждая, что ошибка возникла из-за отсутствующего t.id). Я могу заставить его пройти, выполнив t.id = 999 в том же месте.

Я делаю что-то явно не так или это ошибка в одном из пакетов?

Я использую Python 3.5.2, и мои требования.txt:

Flask==1.0.2
Flask-RESTful==0.3.6
Flask-SQLAlchemy==2.3.2
Jinja2==2.10
pytest==3.2.2
pytest-repeat==0.4.1
SQLAlchemy==1.2.8
Werkzeug==0.14.1

Возможно, стоит отметить, что это также не удалось с более ранними версиями большинства этих пакетов (колба 0.12, sqlalchemy 1.1.14 и т. Д.).

Также стоит отметить, что при запуске с pytest --count=20 app.py он всегда пропустит или провалит весь счет, т. Е. 20 пропусков или 20 сбоев. Но около половины общих пробегов все равно не удастся.

Вот приложение:

#!/usr/bin/env python3
import json
from flask import Flask
from flask_restful import Api, Resource, fields, marshal, reqparse
from flask_sqlalchemy import SQLAlchemy
import pytest

app = Flask(__name__)
api = Api(app)
db = SQLAlchemy(app)

class Thing(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

thing_fields = {
    'name': fields.String,
    'uri': fields.Url('thing'),
}

class ThingListAPI(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument('name', type=str, location='json')
        super().__init__()

    def post(self):
        args = self.reqparse.parse_args()
        t = Thing(name=args['name'])
        db.session.add(t)
        db.session.commit()
        ### <<< at this point inserting pretty much any statement
        ###     will make the test pass >>>
        return {'thing': marshal(t, thing_fields)}, 201

class ThingAPI(Resource):
    def get(self, id):
        pass

api.add_resource(ThingListAPI, '/things', endpoint='things')
api.add_resource(ThingAPI, '/things/<int:id>', endpoint='thing')

@pytest.fixture
def stub_app():
    app.config['TESTING'] = True
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
    client = app.test_client()
    db.create_all()
    yield client
    db.drop_all()

def test_thing_post(stub_app):
    resp = stub_app.post('/things', data=json.dumps({'name': 'stuff'}),
                         content_type='application/json')
    assert(resp.status_code == 201)

1 Ответ

0 голосов
/ 25 июня 2018

Если вы добавите db.session.refresh(t) в post() после коммита, это решит проблему. Я не знаю, правильно ли это делать (SQLAlchemy довольно сложный, и у меня был небольшой опыт с ним), но это показывает, что состояние t -объекта иногда не обновляется ( иногда потому что, вероятно, существует состояние гонки, а иногда SQLAlchemy , возможно, получает больше машинного времени и извлекает идентификатор как раз вовремя, но иногда нет) после коммита и id -атрибут все еще как-то не существует ( Я имею в виду, для flask , потому что для sqlite он существует, но новое состояние не извлекается из БД).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...