我正在测试使用flask-restful
,sqlalchemy
,flask-sqlalchemy
和factory-boy
创建的API。
我遇到了一个奇怪的问题,在使用GET
的{{1}}请求之前创建的对象可用,但在post / put调用之前创建的对象不可用。
我已经基于factory
建立了一个测试用例类:
flask-testing
测试工厂:
# tests/common.py
from flask_testing import TestCase as FlaskTestCase
from app import create_app
from database import db
from config import TestConfig
class TestCase(FlaskTestCase):
def create_app(self):
return create_app(TestConfig)
def setUp(self):
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
资源:
# tests/factory.py
import factory
from database import db
from ..models import Item
class ItemFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = Item
sqlalchemy_session = db.session
测试:
# resources.py
from flask import request
from flask_restful import Resource
from database import db
from .models import Item
from .serializers import ItemSerializer
class ItemResource(Resource):
def get(self, symbol):
obj = db.session(Item).filter(Item.symbol == symbol).first_or_404()
return ItemSerializer(obj).data
def put(self, symbol):
params = request.get_json(silent=True)
query = db.session(Item).filter(Item.symbol == symbol).update(params)
db.session.commit()
obj = db.session(Item).filter(Item.symbol == symbol).first_or_404()
return ItemSerializer(obj).data
在# tests/test_resources.py
import json
from tests.common import TestCase
from tests.factory import ItemFactory
def parse_response(response):
return json.loads(response.get_data().decode())
class ResourcesTest(TestCase):
def test_get_item(self):
symbol = 'TEST'
ItemFactory(symbol=symbol)
response = self.client.get('/api/v1/items/%s' % symbol)
results = parse_response(response)
self.assertEqual(response.status_code, 200)
self.assertEqual(results['symbol'], symbol)
def test_update_item(self):
symbol = 'TEST'
new_symbol = 'TEST_NEW'
ItemFactory(symbol=symbol)
response = self.client.put('/api/v1/items/%s' % symbol, json={'symbol': new_symbol})
results = parse_response(response)
self.assertEqual(response.status_code, 200)
中,我收到404。在输入test_update_item
之前,检查数据库显示新的self.client.put
已创建。但是,当我们在资源中到达Item
方法时,put
返回一个空数组。
从db.session(Item).query.all()
文档中,我发现了这一段:
flask-testing
我认为问题在于会话的处理方式,但无法提出任何解决方案来确保我的测试通过。另一个有趣的观察结果是,对于
Another gotcha is that Flask-SQLAlchemy also removes the session instance at the end of every request (as should any thread safe application using SQLAlchemy with scoped_session). Therefore the session is cleared along with any objects added to it every time you call client.get() or another client method.
或self.client.put
,如果我将发布的数据更改为self.client.post
,则对象将在会话中可用。
答案 0 :(得分:1)
好的,找到了解决这个问题的复杂方法。我认为调用request.get_json
会在其他地方实例化会话。解决方案是重写json数据的发布方式。
在tests/common.py
中:
import json
from flask_testing import TestCase as FlaskTestCase
from flask.testing import FlaskClient
from app import create_app
from database import db
from config import TestConfig
class TestClient(FlaskClient):
def open(self, *args, **kwargs):
if 'json' in kwargs:
kwargs['data'] = json.dumps(kwargs.pop('json'))
kwargs['content_type'] = 'application/json'
return super(TestClient, self).open(*args, **kwargs)
class TestCase(FlaskTestCase):
def create_app(self):
app = create_app(TestConfig)
app.test_client_class = TestClient
return app
def setUp(self):
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
因此,基本上,我们将测试客户端中的post / put调用中的任何json
kwargs转换为数据,并将content_type
设置为json。在这里(https://stackoverflow.com/a/40688088)偶然发现了这一点。
答案 1 :(得分:0)
您可能需要看一下SQLAlchemy会话处理。
factory_boy文档描述了一些选项:https://factoryboy.readthedocs.io/en/latest/orms.html#managing-sessions
工厂似乎在SQLAlchemy会话中创建对象,但该会话未写入数据库。
当您的代码进入烧瓶端时,该上下文可能正在使用与数据库的另一连接(请参见引用的flask-testing
文档),因此看不到尚未提交的对象。